{"id":11488,"library":"odata-v4-server","title":"OData V4 Server for Node.js","description":"odata-v4-server is a Node.js library for building OData Version 4.0 compliant servers. It allows developers to expose OData services either as a standalone server, an Express router, or as a stream. The package facilitates defining service metadata using TypeScript decorators or a JSON schema, supporting standard OData features like query language parsing ($filter, $orderby, $top, $skip, $select, $expand, $count) and CRUD operations on entity sets. It also supports complex types, navigation properties, actions, and functions. Currently at version 0.2.13, its release cadence appears to be irregular as it's still in pre-1.0 development. A key differentiator is its extensive use of decorators for defining controllers and handling OData query parameters, streamlining the development of data services.","status":"maintenance","version":"0.2.13","language":"javascript","source_language":"en","source_url":"https://github.com/jaystack/odata-v4-server","tags":["javascript","OData","server","V4","typescript"],"install":[{"cmd":"npm install odata-v4-server","lang":"bash","label":"npm"},{"cmd":"yarn add odata-v4-server","lang":"bash","label":"yarn"},{"cmd":"pnpm add odata-v4-server","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Required for parsing OData query language expressions like $filter and $orderby.","package":"odata-v4-parser","optional":false},{"reason":"Needed if setting up metadata via JSON instead of decorators.","package":"odata-v4-service-metadata","optional":true}],"imports":[{"note":"The library primarily uses ES Module syntax and TypeScript decorators. While CJS might work in some setups, ESM is the intended and idiomatic way.","wrong":"const { ODataServer } = require('odata-v4-server');","symbol":"ODataServer","correct":"import { ODataServer } from 'odata-v4-server';"},{"note":"This is a named export. Ensure to use destructuring `{ ODataController }`.","wrong":"import ODataController from 'odata-v4-server';","symbol":"ODataController","correct":"import { ODataController } from 'odata-v4-server';"},{"note":"The 'odata' object contains all decorators like `@odata.GET`, `@odata.controller`, `@odata.key`. It's typically imported as a namespace.","wrong":"import { odata } from 'odata-v4-server';","symbol":"odata","correct":"import * as odata from 'odata-v4-server';"},{"note":"This type is used for annotating parameters that receive parsed OData query expressions, such as in `@odata.filter`.","symbol":"ODataQuery","correct":"import { ODataQuery } from 'odata-v4-server';"}],"quickstart":{"code":"import { ODataController, ODataServer, odata, ODataQuery } from 'odata-v4-server';\nimport { MongoClient, ObjectId } from 'mongodb'; // Assuming MongoDB for example persistence\n\n// Dummy data for demonstration\nconst products: any[] = [];\nconst categories: any[] = [];\n\n// Helper to create a filter function (simplified for example)\nconst createFilter = (filter: ODataQuery) => (item: any) => {\n  // In a real application, you'd parse ODataQuery to build a robust filter predicate.\n  // This is a placeholder.\n  console.warn('Filter parsing in createFilter is simplified for demonstration.');\n  return true;\n};\n\nexport class ProductsController extends ODataController {\n    @odata.GET\n    find(@odata.filter filter: ODataQuery) {\n        if (filter) return products.filter(createFilter(filter));\n        return products;\n    }\n\n    @odata.GET\n    findOne(@odata.key key: string) {\n        return products.find(product => product._id === key);\n    }\n\n    @odata.POST\n    insert(@odata.body product: any) {\n        product._id = new ObjectId().toHexString(); // Simulate MongoDB ID\n        products.push(product);\n        return product;\n    }\n\n    @odata.PATCH\n    update(@odata.key key: string, @odata.body delta: any) {\n        let product = products.find(product => product._id === key);\n        if (product) {\n            Object.assign(product, delta);\n        }\n    }\n\n    @odata.DELETE\n    remove(@odata.key key: string) {\n        const index = products.findIndex(product => product._id === key);\n        if (index > -1) {\n            products.splice(index, 1);\n        }\n    }\n}\n\nexport class CategoriesController extends ODataController {\n    @odata.GET\n    find(@odata.filter filter: ODataQuery) {\n        if (filter) return categories.filter(createFilter(filter));\n        return categories;\n    }\n\n    @odata.GET\n    findOne(@odata.key key: string) {\n        return categories.find(category => category._id === key);\n    }\n\n    @odata.POST\n    insert(@odata.body category: any) {\n        category._id = new ObjectId().toHexString();\n        categories.push(category);\n        return category;\n    }\n\n    @odata.PATCH\n    update(@odata.key key: string, @odata.body delta: any) {\n        let category = categories.find(category => category._id === key);\n        if (category) {\n            Object.assign(category, delta);\n        }\n    }\n\n    @odata.DELETE\n    remove(@odata.key key: string) {\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\n// Initialize some dummy data\nproducts.push({ _id: new ObjectId().toHexString(), name: 'Product A', price: 10, categoryId: 'cat1' });\ncategories.push({ _id: 'cat1', name: 'Category X' });\n\nconsole.log('Starting OData server on port 3000 at /odata...');\nNorthwindODataServer.create('/odata', 3000).then(() => {\n    console.log('OData server started. Try accessing:');\n    console.log('  http://localhost:3000/odata');\n    console.log('  http://localhost:3000/odata/$metadata');\n    console.log('  http://localhost:3000/odata/Products');\n    console.log('  http://localhost:3000/odata/Products(\\'<product_id>\\')');\n}).catch(err => console.error('Failed to start OData server:', err));\n","lang":"typescript","description":"This quickstart demonstrates how to set up a basic OData V4 server with two controllers (Products and Categories) handling standard CRUD operations, using TypeScript decorators for routing and parameter injection. It includes a minimal data store simulation and server initialization."},"warnings":[{"fix":"Always pin to exact versions (e.g., `\"odata-v4-server\": \"0.2.13\"`) and review release notes for any update to mitigate unexpected breaking changes. Thorough testing is recommended for any upgrade.","message":"The package is still in pre-1.0 development (version 0.2.13), meaning API surface and internal implementations might change frequently with minor version bumps. Backward compatibility is not guaranteed across minor versions.","severity":"gotcha","affected_versions":">=0.x.x"},{"fix":"For TypeScript users, add or ensure `\"experimentalDecorators\": true` and `\"emitDecoratorMetadata\": true` are present in your `tsconfig.json`. For JavaScript users, configure Babel with the necessary decorator plugins.","message":"This library heavily relies on TypeScript decorators. If you are using JavaScript, you will need a transpiler (like Babel) with decorator support. For TypeScript, `\"experimentalDecorators\": true` and `\"emitDecoratorMetadata\": true` must be enabled in `tsconfig.json`.","severity":"breaking","affected_versions":">=0.x.x"},{"fix":"Replace placeholder ID generation (`new ObjectID().toString()`) with your database's actual ID generation mechanism or ORM's entity management for real-world applications.","message":"The example code often uses `ObjectID` from `mongodb` or similar for key generation. When integrating with a specific database, ensure proper ID generation and persistence logic is implemented in your controller methods, replacing the dummy data logic.","severity":"gotcha","affected_versions":">=0.x.x"},{"fix":"Thoroughly implement the `createFilter` (or similar) function to correctly interpret the `ODataQuery` object and apply it to your data source (e.g., SQL queries, NoSQL queries, in-memory filtering).","message":"OData query parsing (e.g., for `$filter`) hands over a parsed `ODataQuery` object to the controller. Developers are responsible for implementing the logic to translate this parsed query into database queries or filtering operations. The example provides only a simplified placeholder.","severity":"gotcha","affected_versions":">=0.x.x"}],"env_vars":null,"last_verified":"2026-04-19T00:00:00.000Z","next_check":"2026-07-18T00:00:00.000Z","problems":[{"fix":"Install `reflect-metadata` (`npm i reflect-metadata`) and import it once at the entry point of your application (`import 'reflect-metadata';`). Also, ensure `\"emitDecoratorMetadata\": true` is set in `tsconfig.json`.","cause":"Missing `reflect-metadata` polyfill or incorrect `tsconfig.json` settings for decorators.","error":"ReferenceError: Reflect is not defined"},{"fix":"Ensure `odata-v4-server` is installed (`npm i odata-v4-server`). If using an older TypeScript version or complex project setup, verify `node_modules/@types/odata-v4-server` (if it existed) or `node_modules/odata-v4-server/dist/index.d.ts` is correctly resolved by your `tsconfig.json`.","cause":"Package not installed, or TypeScript cannot find its declaration files.","error":"error TS2307: Cannot find module 'odata-v4-server' or its corresponding type declarations."},{"fix":"Add `\"experimentalDecorators\": true` to the `\"compilerOptions\"` section of your `tsconfig.json`.","cause":"`experimentalDecorators` is not enabled in `tsconfig.json`.","error":"Error: Decorators are not enabled. You must enable them in your TypeScript configuration (experimentalDecorators: true)."},{"fix":"Verify that your server class properly `extends ODataServer` and that all decorators (`@odata.controller`, `@odata.cors`) are applied and transpiled correctly. Ensure `reflect-metadata` is imported at the top of your entry file.","cause":"Trying to call `ODataServer.create` without ensuring `NorthwindODataServer` extends `ODataServer` and is correctly decorated, or a transpilation issue.","error":"TypeError: Cannot read properties of undefined (reading 'create')"}],"ecosystem":"npm"}