Install & Compatibility
Where this runs
tested against v? · npm install
Install × environment matrix
Each cell = how many times install + import succeeded across repeated harness runs. Partial = flaky.
glibc = Debian/Ubuntu slim · musl = Alpine Linux
muslnode 18–226 runs
build_error
glibcnode 18–226 runs
build_error
Code
Verified usage
Verified import paths — ran on the pinned version, not inferred.
ODataServer
✓ import { ODataServer } from 'odata4-server'
✗ const { ODataServer } = require('odata4-server')
Primary class for defining and creating an OData server instance. The package ships TypeScript types.
ODataController
✓ import { ODataController } from 'odata4-server'
✗ const { ODataController } = require('odata4-server')
Base class for creating OData entity controllers, managing CRUD and custom actions/functions for specific entity sets.
odata
✓ import * as odata from 'odata4-server'
This import pattern is used to access the `@odata` decorators (e.g., `@odata.controller`, `@odata.GET`, `@odata.filter`) for defining server and controller behavior. TypeScript configuration for decorators is required.
This quickstart sets up a basic OData V4 server with two entity sets, 'Products' and 'Categories', demonstrating CRUD operations and OData query capabilities using decorators. It includes mock data and logs requests to the console, listening on port 3000 by default. Requires TypeScript compilation with decorator support.
import { createFilter, ODataController, ODataServer, ODataQuery } from 'odata4-server';
import * as odata from 'odata4-server';
// Mock data for demonstration
interface Product { _id: string; name: string; categoryId: string; }
interface Category { _id: string; name: string; }
const products: Product[] = [
{ _id: '1', name: 'Apples', categoryId: 'cat1' },
{ _id: '2', name: 'Bananas', categoryId: 'cat1' },
{ _id: '3', name: 'Carrots', categoryId: 'cat2' }
];
const categories: Category[] = [
{ _id: 'cat1', name: 'Fruits' },
{ _id: 'cat2', name: 'Vegetables' }
];
// Simple unique ID generator (replace with proper DB IDs in real app)
let nextId = 10;
const generateId = () => (nextId++).toString();
@odata.controller(products, 'Products')
export class ProductsController extends ODataController{
@odata.GET
find(@odata.filter filter: ODataQuery) {
console.log('GET /Products', filter);
if (filter) return products.filter(createFilter(filter));
return products;
}
@odata.GET
findOne(@odata.key key: string) {
console.log(`GET /Products(${key})`);
return products.filter(product => product._id === key)[0];
}
@odata.POST
insert(@odata.body product: any) {
console.log('POST /Products', product);
product._id = generateId();
products.push(product);
return product;
}
@odata.PATCH
update(@odata.key key: string, @odata.body delta: any) {
console.log(`PATCH /Products(${key})`, delta);
let product = products.filter(product => product._id === key)[0];
if (product) {
for (let prop in delta) {
if (Object.prototype.hasOwnProperty.call(delta, prop)) {
(product as any)[prop] = delta[prop];
}
}
}
}
@odata.DELETE
remove(@odata.key key: string) {
console.log(`DELETE /Products(${key})`);
const index = products.findIndex(product => product._id === key);
if (index > -1) {
products.splice(index, 1);
}
}
}
@odata.controller(categories, 'Categories')
export class CategoriesController extends ODataController{
@odata.GET
find(@odata.filter filter:ODataQuery) {
console.log('GET /Categories', filter);
if (filter) return categories.filter(createFilter(filter));
return categories;
}
@odata.GET
findOne(@odata.key key:string) {
console.log(`GET /Categories(${key})`);
return categories.filter(category => category._id === key)[0];
}
@odata.POST
insert(@odata.body category:any) {
console.log('POST /Categories', category);
category._id = generateId();
categories.push(category);
return category;
}
@odata.PATCH
update(@odata.key key:string, @odata.body delta:any) {
console.log(`PATCH /Categories(${key})`, delta);
let category = categories.filter(category => category._id === key)[0];
if (category) {
for (let prop in delta) {
if (Object.prototype.hasOwnProperty.call(delta, prop)) {
(category as any)[prop] = delta[prop];
}
}
}
}
@odata.DELETE
remove(@odata.key key:string) {
console.log(`DELETE /Categories(${key})`);
const index = categories.findIndex(category => category._id === key);
if (index > -1) {
categories.splice(index, 1);
}
}
}
@odata.cors
@odata.controller(ProductsController, true)
@odata.controller(CategoriesController, true)
export class NorthwindODataServer extends ODataServer{}
const PORT = process.env.PORT || 3000;
NorthwindODataServer.create('/odata', PORT);
console.log(`OData server listening on http://localhost:${PORT}/odata`);
console.log(`Access metadata at http://localhost:${PORT}/odata/$metadata`);
console.log(`Try: http://localhost:${PORT}/odata/Products?$filter=name eq 'Apples'`);
console.log(`Try: http://localhost:${PORT}/odata/Categories('cat1')`);
Errors
Common errors & fixes
ReferenceError: ODataServer is not defined
Attempting to use CommonJS `require` syntax for a module primarily designed for ES Modules or TypeScript imports, or incorrect destructuring.
fixEnsure you are using `import { ODataServer } from 'odata4-server';` at the top of your file, and that your project is configured for ES Modules or TypeScript compilation. TypeError: Decorators are not valid here.
The TypeScript compiler is not configured to process decorators, or they are being used in a JavaScript file without transpilation.
fixIn your `tsconfig.json`, ensure `compilerOptions.experimentalDecorators` and `compilerOptions.emitDecoratorMetadata` are both set to `true`. If using JavaScript, ensure a transpiler like Babel is configured with decorator support.
Cannot GET /odata/$metadata (or other OData endpoints)
The OData server instance was not correctly created or is not listening on the expected port/path, or the port is already in use.
fixVerify `NorthwindODataServer.create('/odata', 3000);` is called. Check console for server startup messages and port conflicts. Ensure you are accessing the correct path (e.g., `http://localhost:3000/odata/$metadata`). Audit
Dependencies
expressoptionalOptional: Needed if the OData server is used as an Express router.