{"id":16870,"library":"odata4-server","title":"OData V4 Server for Node.js","description":"odata4-server is an abandoned Node.js library for creating OData V4 compliant servers, originally forked from the equally unmaintained `jaystack/odata-v4-server`. It provides a decorator-driven approach to define OData controllers and expose service metadata (`$metadata`), supporting a wide range of OData query language features like filtering (`$filter`), sorting (`$orderby`), paging (`$skip`, `$top`), projection (`$select`), and expansion (`$expand`). It can operate as a standalone server, an Express router, or a Node.js stream. The package is currently at version 0.3.0, with its last update more than seven years ago (as of April 2026), indicating it is no longer actively maintained. Due to its abandoned status, it's not recommended for new projects or production use.","status":"abandoned","version":"0.3.0","language":"javascript","source_language":"en","source_url":null,"tags":["javascript","OData","server","V4","typescript"],"install":[{"cmd":"npm install odata4-server","lang":"bash","label":"npm"},{"cmd":"yarn add odata4-server","lang":"bash","label":"yarn"},{"cmd":"pnpm add odata4-server","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Optional: Needed if the OData server is used as an Express router.","package":"express","optional":true}],"imports":[{"note":"Primary class for defining and creating an OData server instance. The package ships TypeScript types.","wrong":"const { ODataServer } = require('odata4-server')","symbol":"ODataServer","correct":"import { ODataServer } from 'odata4-server'"},{"note":"Base class for creating OData entity controllers, managing CRUD and custom actions/functions for specific entity sets.","wrong":"const { ODataController } = require('odata4-server')","symbol":"ODataController","correct":"import { ODataController } from 'odata4-server'"},{"note":"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.","symbol":"odata","correct":"import * as odata from 'odata4-server'"}],"quickstart":{"code":"import { createFilter, ODataController, ODataServer, ODataQuery } from 'odata4-server';\nimport * as odata from 'odata4-server';\n\n// Mock data for demonstration\ninterface Product { _id: string; name: string; categoryId: string; }\ninterface Category { _id: string; name: string; }\n\nconst products: Product[] = [\n  { _id: '1', name: 'Apples', categoryId: 'cat1' },\n  { _id: '2', name: 'Bananas', categoryId: 'cat1' },\n  { _id: '3', name: 'Carrots', categoryId: 'cat2' }\n];\n\nconst categories: Category[] = [\n  { _id: 'cat1', name: 'Fruits' },\n  { _id: 'cat2', name: 'Vegetables' }\n];\n\n// Simple unique ID generator (replace with proper DB IDs in real app)\nlet nextId = 10;\nconst generateId = () => (nextId++).toString();\n\n@odata.controller(products, 'Products')\nexport class ProductsController extends ODataController{\n    @odata.GET\n    find(@odata.filter filter: ODataQuery) {\n        console.log('GET /Products', filter);\n        if (filter) return products.filter(createFilter(filter));\n        return products;\n    }\n\n    @odata.GET\n    findOne(@odata.key key: string) {\n        console.log(`GET /Products(${key})`);\n        return products.filter(product => product._id === key)[0];\n    }\n\n    @odata.POST\n    insert(@odata.body product: any) {\n        console.log('POST /Products', product);\n        product._id = generateId();\n        products.push(product);\n        return product;\n    }\n\n    @odata.PATCH\n    update(@odata.key key: string, @odata.body delta: any) {\n        console.log(`PATCH /Products(${key})`, delta);\n        let product = products.filter(product => product._id === key)[0];\n        if (product) {\n            for (let prop in delta) {\n                if (Object.prototype.hasOwnProperty.call(delta, prop)) {\n                    (product as any)[prop] = delta[prop];\n                }\n            }\n        }\n    }\n\n    @odata.DELETE\n    remove(@odata.key key: string) {\n        console.log(`DELETE /Products(${key})`);\n        const index = products.findIndex(product => product._id === key);\n        if (index > -1) {\n            products.splice(index, 1);\n        }\n    }\n}\n\n@odata.controller(categories, 'Categories')\nexport class CategoriesController extends ODataController{\n    @odata.GET\n    find(@odata.filter filter:ODataQuery) {\n        console.log('GET /Categories', filter);\n        if (filter) return categories.filter(createFilter(filter));\n        return categories;\n    }\n\n    @odata.GET\n    findOne(@odata.key key:string) {\n        console.log(`GET /Categories(${key})`);\n        return categories.filter(category => category._id === key)[0];\n    }\n\n    @odata.POST\n    insert(@odata.body category:any) {\n        console.log('POST /Categories', category);\n        category._id = generateId();\n        categories.push(category);\n        return category;\n    }\n\n    @odata.PATCH\n    update(@odata.key key:string, @odata.body delta:any) {\n        console.log(`PATCH /Categories(${key})`, delta);\n        let category = categories.filter(category => category._id === key)[0];\n        if (category) {\n            for (let prop in delta) {\n                if (Object.prototype.hasOwnProperty.call(delta, prop)) {\n                    (category as any)[prop] = delta[prop];\n                }\n            }\n        }\n    }\n\n    @odata.DELETE\n    remove(@odata.key key:string) {\n        console.log(`DELETE /Categories(${key})`);\n        const index = categories.findIndex(category => category._id === key);\n        if (index > -1) {\n            categories.splice(index, 1);\n        }\n    }\n}\n\n@odata.cors\n@odata.controller(ProductsController, true)\n@odata.controller(CategoriesController, true)\nexport class NorthwindODataServer extends ODataServer{}\n\nconst PORT = process.env.PORT || 3000;\nNorthwindODataServer.create('/odata', PORT);\nconsole.log(`OData server listening on http://localhost:${PORT}/odata`);\nconsole.log(`Access metadata at http://localhost:${PORT}/odata/$metadata`);\nconsole.log(`Try: http://localhost:${PORT}/odata/Products?$filter=name eq 'Apples'`);\nconsole.log(`Try: http://localhost:${PORT}/odata/Categories('cat1')`);\n","lang":"typescript","description":"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."},"warnings":[{"fix":"Consider modern, actively maintained OData server implementations for Node.js, such as SAP CAP Framework or other community-driven projects.","message":"The `odata4-server` package is abandoned, with its last release (v0.3.0) over seven years ago. This means no new features, bug fixes, or security patches will be provided. It is not suitable for new projects or production environments.","severity":"breaking","affected_versions":">=0.3.0"},{"fix":"Run on older Node.js versions if absolutely necessary, but migration to a maintained library is strongly advised. Thorough testing is required for any environment outside its original release context.","message":"Due to its abandonment, `odata4-server` may not be compatible with newer versions of Node.js (e.g., v16, v18, v20+) or updated OData V4 specifications. Breaking changes in Node.js runtime or dependency updates could cause unexpected behavior or crashes.","severity":"breaking","affected_versions":">=0.3.0"},{"fix":"Add `\"experimentalDecorators\": true` and `\"emitDecoratorMetadata\": true` under the `\"compilerOptions\"` section in your `tsconfig.json`.","message":"Using TypeScript decorators (like `@odata.GET`, `@odata.controller`) requires specific compiler options in your `tsconfig.json`, namely `\"experimentalDecorators\": true` and `\"emitDecoratorMetadata\": true`.","severity":"gotcha","affected_versions":">=0.3.0"},{"fix":"Do not use this package in any environment where security is a concern. Migrate to a well-maintained OData solution.","message":"As an abandoned project, `odata4-server` is highly susceptible to unpatched security vulnerabilities. Any issues discovered in its codebase or its underlying dependencies will not be addressed, posing a significant security risk for applications using it.","severity":"gotcha","affected_versions":">=0.3.0"}],"env_vars":null,"last_verified":"2026-04-22T00:00:00.000Z","next_check":"2026-07-21T00:00:00.000Z","problems":[{"fix":"Ensure 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.","cause":"Attempting to use CommonJS `require` syntax for a module primarily designed for ES Modules or TypeScript imports, or incorrect destructuring.","error":"ReferenceError: ODataServer is not defined"},{"fix":"In 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.","cause":"The TypeScript compiler is not configured to process decorators, or they are being used in a JavaScript file without transpilation.","error":"TypeError: Decorators are not valid here."},{"fix":"Verify `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`).","cause":"The OData server instance was not correctly created or is not listening on the expected port/path, or the port is already in use.","error":"Cannot GET /odata/$metadata (or other OData endpoints)"}],"ecosystem":"npm","meta_description":null}