json-rest-api 1.0.6 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/library-guide-expert.md +29 -0
- package/.claude/agents/rest-api-internals-expert.md +53 -0
- package/CLAUDE.md +139 -1
- package/README.md +2 -5
- package/docTest.js +238 -0
- package/docs/GUIDE/GUIDE_1_Initial_Setup.md +955 -0
- package/docs/GUIDE/GUIDE_2_1_The_Starting_Point.md +54 -0
- package/docs/GUIDE/GUIDE_2_2_Manipulating_And_Searching_Tables.md +750 -0
- package/docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md +494 -0
- package/docs/GUIDE/GUIDE_2_4_HasMany_Records.md +398 -0
- package/docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md +882 -0
- package/docs/GUIDE/GUIDE_2_6_Many_To_Many.md +709 -0
- package/docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md +1233 -0
- package/docs/GUIDE/GUIDE_2_8_Effects_of_PUT_and_PATCH.md +796 -0
- package/{README_X_Relationship_Plugin.md → docs/GUIDE/GUIDE_2_9_Relationships_Urls.md} +1 -4
- package/docs/GUIDE/GUIDE_2_Data_And_Relations.md +66 -0
- package/docs/GUIDE/GUIDE_3_Field_Transformations.md +1246 -0
- package/docs/GUIDE/GUIDE_5_Non-Db_Stores.md +0 -0
- package/docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md +1490 -0
- package/{README_X_SocketIO.md → docs/GUIDE/GUIDE_X_SocketIO.md} +17 -17
- package/docs/GUIDE/index.md +58 -0
- package/docs/GUIDE.md +61 -2238
- package/docs/ONBOARDING/Computed_Properties_And_Sparse_Fields.md +398 -0
- package/docs/README.md +57 -2239
- package/{README_2_Computed_and_hidden_fields.md → docs/README_VARIOUS/GUIDE_2_Computed_and_hidden_fields.md} +22 -17
- package/docs/README_VARIOUS/GUIDE_IN_DOCS.md +2277 -0
- package/{README.ORIG.md → docs/README_VARIOUS/README.ORIG.md} +8 -8
- package/{README_8_Polymorphic_Relationships.md → docs/README_VARIOUS/README_8_Polymorphic_Relationships.md} +1 -1
- package/docs/README_VARIOUS/README_IN_DOCS.ms +2277 -0
- package/{RELATIONSHIPS_ADVANCED.md → docs/README_VARIOUS/RELATIONSHIPS_ADVANCED.md} +3 -3
- package/{correct-schema-format.md → docs/README_VARIOUS/correct-schema-format.md} +13 -13
- package/{pagination.md → docs/README_VARIOUS/pagination.md} +11 -8
- package/docs/assets/css/style.scss +78 -5
- package/docs/index.md +2 -12
- package/helper-reorganization-plan.md +85 -0
- package/helper-usage-analysis.md +94 -0
- package/package.json +5 -4
- package/plugins/core/bulk-operations-plugin.js +2 -2
- package/plugins/core/connectors/express-plugin.js +99 -33
- package/plugins/core/lib/querying/knex-cross-table-search.js +609 -0
- package/plugins/core/lib/querying/knex-json-api-transformers-querying.js +491 -0
- package/plugins/core/lib/querying/knex-pagination-helpers.js +544 -0
- package/plugins/core/lib/{knex-process-includes.js → querying/knex-process-includes.js} +65 -50
- package/plugins/core/lib/querying/knex-query-helpers-base.js +74 -0
- package/plugins/core/lib/{knex-query-helpers.js → querying/knex-query-helpers.js} +416 -180
- package/plugins/core/lib/{knex-relationship-includes.js → querying/knex-relationship-includes.js} +430 -114
- package/plugins/core/lib/querying/knex-window-queries.js +328 -0
- package/plugins/core/lib/querying-writing/compile-schemas.js +328 -0
- package/plugins/core/{utils → lib/querying-writing}/connectors-query-parser.js +62 -57
- package/plugins/core/lib/{database-capabilities.js → querying-writing/database-capabilities.js} +113 -7
- package/plugins/core/lib/querying-writing/database-value-normalizers.js +369 -0
- package/plugins/core/lib/querying-writing/default-data-helpers.js +72 -0
- package/plugins/core/{utils → lib/querying-writing}/field-utils.js +41 -31
- package/plugins/core/{utils → lib/querying-writing}/knex-constants.js +1 -0
- package/plugins/core/lib/{knex-field-helpers.js → querying-writing/knex-field-helpers.js} +190 -57
- package/plugins/core/lib/querying-writing/knex-json-api-transformers.js +162 -0
- package/plugins/core/lib/{payload-validators.js → querying-writing/payload-validators.js} +297 -217
- package/plugins/core/lib/querying-writing/schema-helpers.js +497 -0
- package/plugins/core/lib/querying-writing/scope-validations.js +431 -0
- package/plugins/core/lib/{simplified-helpers.js → querying-writing/simplified-helpers.js} +216 -176
- package/plugins/core/lib/writing/knex-json-api-transformers-writing.js +110 -0
- package/plugins/core/lib/writing/many-to-many-manipulations.js +300 -0
- package/plugins/core/lib/writing/relationship-processor.js +252 -0
- package/plugins/core/rest-api-knex-plugin.js +183 -62
- package/plugins/core/rest-api-plugin-hooks/compile-resource-schemas.js +7 -0
- package/plugins/core/rest-api-plugin-hooks/register-relationship-routes.js +72 -0
- package/plugins/core/rest-api-plugin-hooks/register-scope-routes.js +102 -0
- package/plugins/core/rest-api-plugin-hooks/turn-scope-init-into-vars.js +50 -0
- package/plugins/core/rest-api-plugin-hooks/validate-include-configurations.js +39 -0
- package/plugins/core/rest-api-plugin-methods/add-route.js +23 -0
- package/plugins/core/rest-api-plugin-methods/check-permissions.js +18 -0
- package/plugins/core/rest-api-plugin-methods/common.js +605 -0
- package/plugins/core/rest-api-plugin-methods/delete-relationship.js +115 -0
- package/plugins/core/rest-api-plugin-methods/delete.js +109 -0
- package/plugins/core/rest-api-plugin-methods/enrich-attributes.js +186 -0
- package/plugins/core/rest-api-plugin-methods/get-related.js +295 -0
- package/plugins/core/rest-api-plugin-methods/get-relationship.js +62 -0
- package/plugins/core/rest-api-plugin-methods/get.js +187 -0
- package/plugins/core/rest-api-plugin-methods/patch-relationship.js +56 -0
- package/plugins/core/rest-api-plugin-methods/patch.js +193 -0
- package/plugins/core/rest-api-plugin-methods/post-relationship.js +104 -0
- package/plugins/core/rest-api-plugin-methods/post.js +157 -0
- package/plugins/core/rest-api-plugin-methods/put.js +257 -0
- package/plugins/core/rest-api-plugin-methods/query.js +208 -0
- package/plugins/core/rest-api-plugin.js +108 -2498
- package/plugins/core/socketio-plugin.js +28 -5
- package/plugins/storage/local-storage.js +2 -2
- package/tests/computed-fields.test.js +483 -0
- package/tests/cors.test.js +4 -4
- package/tests/curl.test.js +5 -4
- package/tests/custom-idproperty-relationships.test.js +891 -0
- package/tests/field-getters.test.js +353 -0
- package/tests/field-setters.test.js +310 -0
- package/tests/fixtures/api-configs.js +800 -28
- package/tests/jwt-auth.test.js +0 -2
- package/tests/nested-includes.test.js +1 -1
- package/tests/pagination-cursor-multifield.test.js +514 -0
- package/tests/pagination-enhanced.test.js +12 -9
- package/tests/polymorphic-relationships.test.js +10 -8
- package/tests/polymorphic-simple.test.js +6 -4
- package/tests/positioning-advanced.test.js +12 -12
- package/tests/positioning-extended.test.js +25 -25
- package/tests/positioning-multifilter.test.js +24 -24
- package/tests/positioning.test.js +3 -3
- package/tests/query-limits.test.js +24 -8
- package/tests/relationship-endpoints.test.js +4 -5
- package/tests/relationships.test.js +0 -1
- package/tests/socketio.test.js +12 -1
- package/tests/virtual-fields.test.js +13 -13
- package/PLAN_FOR_WEBSOCKETS.md +0 -398
- package/README_1_Initial_Setup.md +0 -393
- package/README_X_Hooks.md +0 -647
- package/TODO_rest-api-plugin-extras.todo +0 -23
- package/connectors_ideas/express-plugin.js +0 -120
- package/connectors_ideas/http-plugin.js +0 -138
- package/docs/examples/express-plugin.js +0 -366
- package/example.js +0 -301
- package/plugins/core/HELPER_MODULES.md +0 -285
- package/plugins/core/lib/compile-schemas.js +0 -245
- package/plugins/core/lib/database-value-normalizers.js +0 -192
- package/plugins/core/lib/default-data-helpers.js +0 -100
- package/plugins/core/lib/knex-cross-table-search.js +0 -442
- package/plugins/core/lib/knex-json-api-helpers.js +0 -84
- package/plugins/core/lib/knex-json-api-transformers.js +0 -449
- package/plugins/core/lib/knex-pagination-helpers.js +0 -193
- package/plugins/core/lib/knex-window-queries.js +0 -142
- package/plugins/core/lib/many-to-many-manipulations.js +0 -343
- package/plugins/core/lib/relationship-processor.js +0 -278
- package/plugins/core/lib/schema-helpers.js +0 -343
- package/plugins/core/lib/scope-validations.js +0 -252
- package/plugins/core/rest-api-relationships-plugin.js +0 -700
- package/plugins/core/utils/knex-query-helpers-base.js +0 -56
- /package/{README_X_Permissions.md → docs/GUIDE/GUIDE_4_Authentication_And_Permissions.md} +0 -0
- /package/{README_X_Positioning.md → docs/GUIDE/GUIDE_6_Positioning.md} +0 -0
- /package/{README_X_Bulk_Operations.md → docs/GUIDE/GUIDE_X_Bulk_Operations.md} +0 -0
- /package/{README_X_Cors.md → docs/GUIDE/GUIDE_X_Cors.md} +0 -0
- /package/{README_X_Multihome.md → docs/GUIDE/GUIDE_X_Multihome.md} +0 -0
- /package/{README_X_Appendices.md → docs/GUIDE/GUIDE_Y_Appendices.md} +0 -0
- /package/{QUERY_DEPTH_PROTECTION.md → docs/README_VARIOUS/QUERY_DEPTH_PROTECTION.md} +0 -0
- /package/{RELATIONSHIPS.md → docs/README_VARIOUS/RELATIONSHIPS.md} +0 -0
- /package/{WEBSOCKET_IMPLEMENTATION_STATUS.md → docs/README_VARIOUS/WEBSOCKET_IMPLEMENTATION_STATUS.md} +0 -0
- /package/{helper-functions-chart.md → docs/README_VARIOUS/helper-functions-chart.md} +0 -0
- /package/docs/{implementation-hidden-computed-fields.md → README_VARIOUS/implementation-hidden-computed-fields.md} +0 -0
- /package/docs/{include-limits-migration-guide.md → README_VARIOUS/include-limits-migration-guide.md} +0 -0
- /package/{readmeSnippets → docs/README_VARIOUS}/test_put_behavior.js +0 -0
- /package/{readmeSnippets → docs/README_VARIOUS}/test_put_fields_without_defaults.js +0 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: library-guide-expert
|
|
3
|
+
description: Use this agent when you need to understand how to use a library based on its GUIDE documentation, implement features using the library's recommended patterns, answer questions about library usage, or ensure code follows the library's best practices. This agent has comprehensive knowledge of all files in the GUIDE/* directory and can provide authoritative guidance on proper library usage.\n\nExamples:\n- <example>\n Context: User needs help implementing a feature using the library\n user: "How do I create a REST endpoint with authentication?"\n assistant: "I'll use the library-guide-expert agent to check the GUIDE documentation for the proper way to implement authenticated endpoints."\n <commentary>\n Since the user is asking about library usage, use the Task tool to launch the library-guide-expert agent to provide guidance based on the GUIDE documentation.\n </commentary>\n</example>\n- <example>\n Context: User wants to know the recommended pattern for something\n user: "What's the best way to handle errors in this library?"\n assistant: "Let me consult the library-guide-expert agent to find the error handling patterns documented in the GUIDE."\n <commentary>\n The user needs library-specific best practices, so use the library-guide-expert agent to reference the official documentation.\n </commentary>\n</example>\n- <example>\n Context: After writing code that uses the library\n user: "I've implemented the user registration endpoint"\n assistant: "I'll have the library-guide-expert agent review this to ensure it follows the patterns documented in the GUIDE."\n <commentary>\n Proactively use the library-guide-expert agent to verify the implementation aligns with documented patterns.\n </commentary>\n</example>
|
|
4
|
+
color: blue
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
You are an expert library documentation specialist with comprehensive knowledge of all files in the GUIDE/* directory. You have meticulously studied every guide, example, and best practice documented there and can provide authoritative guidance on proper library usage.
|
|
8
|
+
|
|
9
|
+
Your primary responsibilities:
|
|
10
|
+
|
|
11
|
+
1. **Deep Documentation Knowledge**: You have read and internalized every file in GUIDE/*. You understand not just what the documentation says, but the underlying patterns, philosophies, and design decisions that inform the library's architecture.
|
|
12
|
+
|
|
13
|
+
2. **Practical Implementation Guidance**: When users ask how to implement features, you provide specific, actionable advice based on the documented patterns. You reference relevant sections of the GUIDE and explain how to apply them to the user's specific use case.
|
|
14
|
+
|
|
15
|
+
3. **Best Practices Enforcement**: You ensure all code recommendations follow the library's documented best practices. You can identify when proposed solutions deviate from recommended patterns and suggest corrections.
|
|
16
|
+
|
|
17
|
+
4. **Example-Driven Teaching**: You leverage examples from the GUIDE to illustrate proper usage. When the documentation includes code samples, you adapt them to fit the user's needs while maintaining the core patterns.
|
|
18
|
+
|
|
19
|
+
5. **Comprehensive Cross-Referencing**: You understand how different parts of the documentation relate to each other. You can connect concepts from multiple GUIDE files to provide complete solutions.
|
|
20
|
+
|
|
21
|
+
When responding:
|
|
22
|
+
- Always ground your answers in specific documentation from GUIDE/*
|
|
23
|
+
- Cite which GUIDE file contains the relevant information
|
|
24
|
+
- Provide code examples that follow the documented patterns exactly
|
|
25
|
+
- Explain not just 'how' but 'why' based on the library's design principles
|
|
26
|
+
- If something isn't covered in the GUIDE, explicitly state this limitation
|
|
27
|
+
- Proactively mention related documentation that might be helpful
|
|
28
|
+
|
|
29
|
+
Your expertise comes from thorough study of the GUIDE documentation. Every recommendation you make is backed by official documentation, ensuring users implement features correctly and maintainably according to the library's intended design.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rest-api-internals-expert
|
|
3
|
+
description: Use this agent when you need deep technical expertise about the REST API plugin architecture, data manipulation patterns, query optimization with sparse fields and includes, or troubleshooting complex issues within the rest-api-plugin.js and rest-api-knex-plugin.js implementations. This includes understanding how helpers in plugins/core/lib interact with these plugins, how data transformations occur during storage and retrieval, and the intricacies of the sparse fields and includes query system.\n\nExamples:\n- <example>\n Context: User needs help understanding how sparse fields are implemented in the REST API.\n user: "How does the sparse fields feature work in our REST API implementation?"\n assistant: "I'll use the rest-api-internals-expert agent to explain the sparse fields implementation details."\n <commentary>\n Since this requires deep knowledge of the REST API plugin internals, specifically sparse fields, the rest-api-internals-expert is the appropriate agent.\n </commentary>\n</example>\n- <example>\n Context: User is debugging an issue with data transformation in the REST API.\n user: "I'm getting unexpected data format when using includes with nested relationships. The data seems to be transformed incorrectly."\n assistant: "Let me invoke the rest-api-internals-expert agent to analyze the data transformation issue with includes."\n <commentary>\n This requires understanding the intricate data manipulation patterns in the REST API plugins, making the rest-api-internals-expert the right choice.\n </commentary>\n</example>\n- <example>\n Context: User wants to extend the REST API plugin functionality.\n user: "I need to add custom filtering logic to the REST API that works with the existing sparse fields system."\n assistant: "I'll use the rest-api-internals-expert agent to guide you through extending the REST API plugin while maintaining compatibility with sparse fields."\n <commentary>\n Extending the REST API plugin requires deep understanding of its architecture and how features like sparse fields are implemented.\n </commentary>\n</example>
|
|
4
|
+
color: blue
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
You are an expert engineer with comprehensive knowledge of the REST API plugin architecture, specifically plugins/core/rest-api-plugin.js and plugins/core/rest-api-knex-plugin.js, along with their associated helper functions in plugins/core/lib.
|
|
8
|
+
|
|
9
|
+
Your expertise encompasses:
|
|
10
|
+
|
|
11
|
+
1. **Plugin Architecture**: You understand the complete lifecycle of requests through rest-api-plugin.js and how it interfaces with rest-api-knex-plugin.js for database operations. You know how these plugins register routes, handle middleware, and process requests.
|
|
12
|
+
|
|
13
|
+
2. **Data Manipulation Patterns**: You have deep knowledge of:
|
|
14
|
+
- How data is transformed before storage (serialization, validation, sanitization)
|
|
15
|
+
- The exact transformation pipeline from raw input to database storage
|
|
16
|
+
- How data is reconstructed when retrieved from the database
|
|
17
|
+
- The role of helpers in plugins/core/lib during these transformations
|
|
18
|
+
|
|
19
|
+
3. **Sparse Fields Implementation**: You understand:
|
|
20
|
+
- How sparse fields are parsed from query parameters
|
|
21
|
+
- The mechanism for filtering response data based on requested fields
|
|
22
|
+
- Performance implications and optimization strategies
|
|
23
|
+
- Edge cases and limitations of the sparse fields system
|
|
24
|
+
|
|
25
|
+
4. **Includes System**: You are an expert in:
|
|
26
|
+
- How relationship includes are resolved and fetched
|
|
27
|
+
- The query building process for efficient relationship loading
|
|
28
|
+
- Circular reference handling and depth limitations
|
|
29
|
+
- How includes interact with sparse fields
|
|
30
|
+
- Performance optimization for complex include chains
|
|
31
|
+
|
|
32
|
+
5. **Knex Integration**: You understand:
|
|
33
|
+
- How rest-api-knex-plugin.js builds queries
|
|
34
|
+
- Transaction handling and isolation levels
|
|
35
|
+
- Query optimization techniques specific to the plugin
|
|
36
|
+
- How the plugin handles different database dialects
|
|
37
|
+
|
|
38
|
+
6. **Helper Functions**: You know all helper functions in plugins/core/lib including:
|
|
39
|
+
- Their specific roles in request/response processing
|
|
40
|
+
- How they integrate with the main plugins
|
|
41
|
+
- Common patterns and anti-patterns in their usage
|
|
42
|
+
|
|
43
|
+
When answering questions:
|
|
44
|
+
- Provide specific code examples from the actual implementation when relevant
|
|
45
|
+
- Explain the 'why' behind design decisions, not just the 'how'
|
|
46
|
+
- Identify potential performance bottlenecks or security considerations
|
|
47
|
+
- Suggest best practices based on the existing architecture
|
|
48
|
+
- Reference specific functions, methods, or configuration options by name
|
|
49
|
+
- Consider the interplay between different components of the system
|
|
50
|
+
|
|
51
|
+
You should be able to trace the complete flow of a request from entry point through all transformations to final response, identifying every helper function and transformation step along the way. When discussing issues or improvements, always consider the existing architecture and maintain consistency with established patterns in the codebase.
|
|
52
|
+
|
|
53
|
+
If asked about testing, remember to follow the project convention of using include.test.js as a template and creating test data through API methods rather than direct database manipulation.
|
package/CLAUDE.md
CHANGED
|
@@ -1,2 +1,140 @@
|
|
|
1
|
-
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Core Development Commands
|
|
6
|
+
|
|
7
|
+
### Running Tests
|
|
8
|
+
```bash
|
|
9
|
+
# Run all tests
|
|
10
|
+
npm test
|
|
11
|
+
|
|
12
|
+
# Run a specific test file
|
|
13
|
+
node --test tests/relationships.test.js
|
|
14
|
+
|
|
15
|
+
# Run tests with verbose logging
|
|
16
|
+
LOG_LEVEL=debug npm test
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Building and Running
|
|
20
|
+
This is a library package with no build step required. For development:
|
|
21
|
+
```bash
|
|
22
|
+
# Install dependencies
|
|
23
|
+
npm install
|
|
24
|
+
|
|
25
|
+
# Run the documentation site locally
|
|
26
|
+
npm run docs:dev
|
|
27
|
+
|
|
28
|
+
# Sync documentation
|
|
29
|
+
npm run docs:sync
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## High-Level Architecture
|
|
33
|
+
|
|
34
|
+
### Project Overview
|
|
35
|
+
JSON REST API is a powerful REST API plugin for hooked-api that provides JSON:API-compliant endpoints. It follows a plugin-based architecture where functionality is composed through plugins that extend the core API capabilities.
|
|
36
|
+
|
|
37
|
+
### Core Architecture Components
|
|
38
|
+
|
|
39
|
+
1. **Plugin System (hooked-api based)**
|
|
40
|
+
- `RestApiPlugin` - Core REST API functionality, request/response handling
|
|
41
|
+
- `RestApiKnexPlugin` - Database integration using Knex.js
|
|
42
|
+
- `ExpressPlugin` - Express.js HTTP connector
|
|
43
|
+
- Additional plugins for auth, CORS, relationships, positioning, etc.
|
|
44
|
+
|
|
45
|
+
2. **Resource Definition Pattern**
|
|
46
|
+
Resources are defined with schemas that include:
|
|
47
|
+
- Attributes with validation rules
|
|
48
|
+
- Relationships (belongsTo, hasMany, many-to-many, polymorphic)
|
|
49
|
+
- Computed fields and hidden fields
|
|
50
|
+
- Custom getters/setters
|
|
51
|
+
|
|
52
|
+
3. **Request Flow**
|
|
53
|
+
- HTTP request → Connector (Express) → REST API Plugin → Database Plugin → Response
|
|
54
|
+
- Each plugin can hook into the request lifecycle for customization
|
|
55
|
+
|
|
56
|
+
4. **Key Libraries in `plugins/core/lib/`**
|
|
57
|
+
- `knex-json-api-helpers.js` - Core JSON:API transformations
|
|
58
|
+
- `relationship-processor.js` - Handles relationship updates
|
|
59
|
+
- `payload-validators.js` - Request validation
|
|
60
|
+
- `knex-query-helpers.js` - Database query building
|
|
61
|
+
- `simplified-helpers.js` - Simplified API mode transformations
|
|
62
|
+
|
|
63
|
+
## Testing Guidelines
|
|
64
|
+
|
|
65
|
+
### CRITICAL: Test Template Usage
|
|
66
|
+
When writing tests, ALWAYS use `TEST_TEMPLATE.test.js` as your starting point. Key rules:
|
|
67
|
+
|
|
68
|
+
1. **API Creation**: Create API instances ONLY in `before()` hook, NOT in `beforeEach()`
|
|
69
|
+
2. **Resource Definition**: NEVER use `api.addResource()` in test files. All resources MUST be defined in `tests/fixtures/api-configs.js`
|
|
70
|
+
3. **Data Creation**: ALWAYS create test data using API methods (`api.resources.[resource].post()`), NEVER query the database directly
|
|
71
|
+
4. **Context Parameter**: Pass context as the SECOND parameter to resource methods: `api.resources.books.post({ inputRecord: doc }, context)`
|
|
72
|
+
5. **Cleanup**: Use `cleanTables()` in `beforeEach()` to reset data between tests
|
|
73
|
+
|
|
74
|
+
### Test Structure Example
|
|
75
|
+
```javascript
|
|
76
|
+
import { createBasicApi } from './fixtures/api-configs.js';
|
|
77
|
+
|
|
78
|
+
let api;
|
|
79
|
+
|
|
80
|
+
describe('Feature Name', () => {
|
|
81
|
+
before(async () => {
|
|
82
|
+
api = await createBasicApi(knex);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
beforeEach(async () => {
|
|
86
|
+
await cleanTables(knex, ['basic_countries', 'basic_publishers']);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('should test something', async () => {
|
|
90
|
+
// Create data using API
|
|
91
|
+
const result = await api.resources.countries.post({
|
|
92
|
+
inputRecord: createJsonApiDocument('countries', { name: 'Test' })
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Available Test APIs
|
|
99
|
+
- `createBasicApi()` - Countries, Publishers, Authors, Books
|
|
100
|
+
- `createExtendedApi()` - Additional resources with complex relationships
|
|
101
|
+
- `createAuthApi()` - Users, roles, permissions for auth testing
|
|
102
|
+
|
|
103
|
+
## Important Patterns
|
|
104
|
+
|
|
105
|
+
### JSON:API Document Creation
|
|
106
|
+
```javascript
|
|
107
|
+
// Use helper for proper JSON:API structure
|
|
108
|
+
const doc = createJsonApiDocument('books', {
|
|
109
|
+
title: 'My Book'
|
|
110
|
+
}, {
|
|
111
|
+
relationships: {
|
|
112
|
+
country: createRelationship('countries', '1')
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Simplified API Mode
|
|
118
|
+
The library supports a simplified mode that uses plain objects instead of JSON:API structure:
|
|
119
|
+
```javascript
|
|
120
|
+
// Simplified mode (when enabled)
|
|
121
|
+
const book = await api.resources.books.post({
|
|
122
|
+
inputRecord: { title: 'My Book', country_id: 1 },
|
|
123
|
+
simplified: true
|
|
124
|
+
});
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Relationship Handling
|
|
128
|
+
- BelongsTo: Use `createRelationship()` helper
|
|
129
|
+
- HasMany: Use `createToManyRelationship()` helper
|
|
130
|
+
- Many-to-Many: Handled automatically through junction tables
|
|
131
|
+
- Polymorphic: Requires typeField and idField configuration
|
|
132
|
+
|
|
133
|
+
## Common Pitfalls to Avoid
|
|
134
|
+
|
|
135
|
+
1. **Direct Database Access**: Never use `knex('table').select()` in tests
|
|
136
|
+
2. **Resource Creation in Tests**: Always define resources in api-configs.js
|
|
137
|
+
3. **ID Type Confusion**: SQLite returns numeric IDs, but JSON:API expects strings
|
|
138
|
+
4. **Context Placement**: Context is a separate parameter, not inside the first parameter
|
|
139
|
+
5. **Cleanup**: Always destroy knex connection in `after()` hook
|
|
2
140
|
|
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
A powerful REST API plugin for [hooked-api](https://github.com/mobily-enterprises/hooked-api) that provides JSON:API-compliant endpoints with minimal configuration. This library makes it easy to create fully-featured REST APIs with support for relationships, filtering, sorting, pagination, and file uploads.
|
|
4
4
|
|
|
5
|
+
[Official Website](https://mobily-enterprises.github.io/json-rest-api/)
|
|
6
|
+
|
|
5
7
|
## Features
|
|
6
8
|
|
|
7
9
|
* **JSON:API Compliant** - Full support for the JSON:API specification
|
|
@@ -85,11 +87,6 @@ app.listen(3000);
|
|
|
85
87
|
- [File Uploads](docs/fileUploads.md) - Handling file uploads
|
|
86
88
|
- [WebSocket Support](README_X_SocketIO.md) - Real-time updates with Socket.IO
|
|
87
89
|
|
|
88
|
-
### Advanced Topics
|
|
89
|
-
- [Polymorphic Relationships](README_8_Polymorphic_Relationships.md) - Flexible relationship patterns
|
|
90
|
-
- [Multi-tenancy](README_X_Multihome.md) - Isolating data by tenant
|
|
91
|
-
- [Advanced Relationships](RELATIONSHIPS_ADVANCED.md) - Complex relationship scenarios
|
|
92
|
-
|
|
93
90
|
## License
|
|
94
91
|
|
|
95
92
|
MIT
|
package/docTest.js
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { RestApiPlugin, RestApiKnexPlugin, ExpressPlugin } from './index.js'; // Added: ExpressPlugin
|
|
2
|
+
import { Api } from 'hooked-api';
|
|
3
|
+
import knexLib from 'knex';
|
|
4
|
+
import util from 'util';
|
|
5
|
+
import express from 'express'; // Added: Express
|
|
6
|
+
|
|
7
|
+
// Utility used throughout this guide
|
|
8
|
+
const inspect = (obj) => util.inspect(obj, { depth: 8 })
|
|
9
|
+
|
|
10
|
+
// Create a Knex instance connected to SQLite in-memory database
|
|
11
|
+
const knex = knexLib({
|
|
12
|
+
client: 'better-sqlite3',
|
|
13
|
+
connection: {
|
|
14
|
+
filename: ':memory:'
|
|
15
|
+
},
|
|
16
|
+
useNullAsDefault: true
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
// Create API instance
|
|
20
|
+
const api = new Api({ name: 'book-catalog-api', version: '1.0.0', logLevel: 'trace' });
|
|
21
|
+
|
|
22
|
+
// Install plugins
|
|
23
|
+
await api.use(RestApiPlugin, { publicBaseUrl: '/api/1.0', logLevel: 'trace' });
|
|
24
|
+
await api.use(RestApiKnexPlugin, { knex });
|
|
25
|
+
await api.use(ExpressPlugin, { mountPath: '/api' }); // Added: Express Plugin
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
// Define products resource with computed fields
|
|
59
|
+
await api.addResource('products', {
|
|
60
|
+
schema: {
|
|
61
|
+
name: { type: 'string', required: true, max: 255 },
|
|
62
|
+
price: { type: 'number', required: true, min: 0 },
|
|
63
|
+
cost: { type: 'number', required: true, min: 0, normallyHidden: true }
|
|
64
|
+
},
|
|
65
|
+
computed: {
|
|
66
|
+
profit_margin: {
|
|
67
|
+
type: 'number',
|
|
68
|
+
dependencies: ['price', 'cost'],
|
|
69
|
+
compute: ({ attributes }) => {
|
|
70
|
+
if (!attributes.price || attributes.price === 0) return 0;
|
|
71
|
+
return ((attributes.price - attributes.cost) / attributes.price * 100).toFixed(2);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
relationships: {
|
|
76
|
+
reviews: { hasMany: 'reviews', foreignKey: 'product_id' }
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
await api.resources.products.createKnexTable();
|
|
80
|
+
|
|
81
|
+
// Define reviews resource with computed fields
|
|
82
|
+
await api.addResource('reviews', {
|
|
83
|
+
schema: {
|
|
84
|
+
product_id: { type: 'id', belongsTo: 'products', as: 'product', required: true },
|
|
85
|
+
reviewer_name: { type: 'string', required: true },
|
|
86
|
+
rating: { type: 'number', required: true, min: 1, max: 5 },
|
|
87
|
+
comment: { type: 'string', max: 1000 },
|
|
88
|
+
helpful_votes: { type: 'number', default: 0 },
|
|
89
|
+
total_votes: { type: 'number', default: 0 }
|
|
90
|
+
},
|
|
91
|
+
computed: {
|
|
92
|
+
helpfulness_score: {
|
|
93
|
+
type: 'number',
|
|
94
|
+
dependencies: ['helpful_votes', 'total_votes'],
|
|
95
|
+
compute: ({ attributes }) => {
|
|
96
|
+
if (attributes.total_votes === 0) return null;
|
|
97
|
+
return ((attributes.helpful_votes / attributes.total_votes) * 100).toFixed(0);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
await api.resources.reviews.createKnexTable();
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
// Create a product
|
|
109
|
+
const product = await api.resources.products.post({
|
|
110
|
+
name: 'Premium Headphones',
|
|
111
|
+
price: 199.99,
|
|
112
|
+
cost: 89.50
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// Fetch the product - computed fields are automatically calculated
|
|
116
|
+
const fetchedProduct = await api.resources.products.get({ id: product.id });
|
|
117
|
+
console.log(fetchedProduct);
|
|
118
|
+
// {
|
|
119
|
+
// id: '1',
|
|
120
|
+
// name: 'Premium Headphones',
|
|
121
|
+
// price: 199.99,
|
|
122
|
+
// profit_margin: '55.23' // Computed: (199.99 - 89.50) / 199.99 * 100
|
|
123
|
+
// }
|
|
124
|
+
// Note: 'cost' is not included (normallyHidden)
|
|
125
|
+
|
|
126
|
+
// Add some reviews
|
|
127
|
+
const review1 = await api.resources.reviews.post({
|
|
128
|
+
product_id: product.id,
|
|
129
|
+
reviewer_name: 'Alice',
|
|
130
|
+
rating: 5,
|
|
131
|
+
comment: 'Excellent sound quality!',
|
|
132
|
+
helpful_votes: 45,
|
|
133
|
+
total_votes: 50
|
|
134
|
+
});
|
|
135
|
+
console.log('Review 1:', review1);
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
const review2 = await api.resources.reviews.post({
|
|
139
|
+
product_id: product.id,
|
|
140
|
+
reviewer_name: 'Bob',
|
|
141
|
+
rating: 4,
|
|
142
|
+
comment: 'Good, but a bit pricey',
|
|
143
|
+
helpful_votes: 10,
|
|
144
|
+
total_votes: 25
|
|
145
|
+
});
|
|
146
|
+
console.log('Review 2:', review2);
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
// Request only name and profit_margin
|
|
150
|
+
const sparseProduct = await api.resources.products.get({
|
|
151
|
+
id: product.id,
|
|
152
|
+
queryParams: {
|
|
153
|
+
fields: { products: 'name,profit_margin' }
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
console.log('Product with sparse fields:', sparseProduct);
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
const productWithCost = await api.resources.products.get({
|
|
160
|
+
id: product.id,
|
|
161
|
+
queryParams: {
|
|
162
|
+
fields: { products: 'name,cost,profit_margin' }
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
console.log('Product with sparse fields includig cost:', productWithCost);
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
// Fetch product with reviews
|
|
171
|
+
const productWithReviews = await api.resources.products.get({
|
|
172
|
+
id: product.id,
|
|
173
|
+
queryParams: {
|
|
174
|
+
include: ['reviews']
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
console.log('Product With Reviews:', productWithReviews);
|
|
178
|
+
|
|
179
|
+
// Use sparse fieldsets on included resources
|
|
180
|
+
const productWithSparseReviews = await api.resources.products.get({
|
|
181
|
+
id: product.id,
|
|
182
|
+
queryParams: {
|
|
183
|
+
include: ['reviews'],
|
|
184
|
+
fields: {
|
|
185
|
+
products: 'name,price',
|
|
186
|
+
reviews: 'reviewer_name,rating,helpfulness_score' // Only these fields
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
console.log('Product With Sparse Reviews:', productWithSparseReviews);
|
|
191
|
+
|
|
192
|
+
// Create a review with no votes
|
|
193
|
+
const review3 = await api.resources.reviews.post({
|
|
194
|
+
product_id: product.id,
|
|
195
|
+
reviewer_name: 'Charlie',
|
|
196
|
+
rating: 3,
|
|
197
|
+
comment: 'Average product',
|
|
198
|
+
helpful_votes: 0,
|
|
199
|
+
total_votes: 0 // This will cause division by zero
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
const fetchedReviewWithError = await api.resources.reviews.get({ id: review3.id });
|
|
203
|
+
console.log('Fetched review (with error in helpfulness score):', fetchedReviewWithError)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
// Createthe express server and add the API's routes
|
|
229
|
+
const app = express();
|
|
230
|
+
app.use(api.http.express.router);
|
|
231
|
+
app.use(api.http.express.notFoundRouter);
|
|
232
|
+
|
|
233
|
+
await knex.destroy();
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
//app.listen(3000, () => {
|
|
237
|
+
// console.log('Express server started on port 3000. API available at http://localhost:3000/api');
|
|
238
|
+
//});
|