feat(samples): add TypeScript backend using Grid TypeScript SDK#197
feat(samples): add TypeScript backend using Grid TypeScript SDK#197
Conversation
This stack of pull requests is managed by Graphite. Learn more about stacking. |
Greptile SummaryAdds a TypeScript (Express) backend sample that mirrors the existing Kotlin (Ktor) sample, implementing the same API contract for the Grid payout flow: customer creation, external account linking, quote creation/execution, sandbox funding, and webhook-to-SSE streaming. The implementation uses the
Confidence Score: 4/5
|
| Filename | Overview |
|---|---|
| samples/typescript/package.json | Project config with Express 5 and Grid SDK. Uses "latest" for @lightsparkdev/grid which could pull breaking versions on fresh installs. Frontend build script shells out to ../frontend to build Vite output into public/. |
| samples/typescript/src/index.ts | Main Express server setup with CORS, routing, SSE streaming, and static file serving. Has an unused config import and an invalid CORS header combination (Allow-Credentials: true with Allow-Origin: *). |
| samples/typescript/src/routes/customers.ts | Customer creation endpoint using gridClient.customers.create(). Follows the same API contract as the Kotlin sample. |
| samples/typescript/src/routes/externalAccounts.ts | External account creation with beneficiary defaults. Provides sensible fallback values for optional fields, matching the Kotlin sample's behavior. |
| samples/typescript/src/routes/quotes.ts | Quote creation and execution endpoints. Passes source/destination objects through to the SDK, which is simpler than the Kotlin sample's typed builder approach but equivalent for a sample app. |
| samples/typescript/src/routes/webhooks.ts | Webhook receiver that broadcasts events via SSE. Skips signature verification with a TODO note about upcoming SDK support. The Kotlin sample does perform verification, but the comment explains the omission. |
| samples/typescript/src/webhookStream.ts | EventEmitter-based webhook event streaming with 10-event replay buffer. Clean equivalent of the Kotlin MutableSharedFlow implementation. |
| samples/typescript/tsconfig.json | TypeScript config targeting ES2022 with ESNext modules and strict mode. Uses moduleResolution: "bundler" which works with tsx runtime though "node16" would be more semantically correct for a Node.js project. |
Sequence Diagram
sequenceDiagram
participant Browser as React Frontend
participant Express as Express Backend (:8080)
participant Grid as Grid API
Browser->>Express: POST /api/customers
Express->>Grid: gridClient.customers.create()
Grid-->>Express: Customer object
Express-->>Browser: 201 Customer JSON
Browser->>Express: POST /api/customers/{id}/external-accounts
Express->>Grid: gridClient.customers.externalAccounts.create()
Grid-->>Express: External account object
Express-->>Browser: 201 Account JSON
Browser->>Express: POST /api/quotes
Express->>Grid: gridClient.quotes.create()
Grid-->>Express: Quote object
Express-->>Browser: 201 Quote JSON
Browser->>Express: POST /api/quotes/{id}/execute
Express->>Grid: gridClient.quotes.execute()
Grid-->>Express: Executed quote
Express-->>Browser: 200 Quote JSON
Browser->>Express: POST /api/sandbox/send-funds
Express->>Grid: gridClient.sandbox.sendFunds()
Grid-->>Express: Funding response
Express-->>Browser: 200 Response JSON
Grid->>Express: POST /api/webhooks (webhook event)
Express->>Express: webhookStream.addEvent()
Express-->>Browser: SSE data event (via GET /api/sse)
Last reviewed commit: bb08f69
| "compilerOptions": { | ||
| "target": "ES2022", | ||
| "module": "ESNext", | ||
| "moduleResolution": "bundler", |
There was a problem hiding this comment.
moduleResolution: "bundler" is intended for bundlers like Vite/esbuild. For Node.js with tsx, use "node" or "node16":
| "moduleResolution": "bundler", | |
| "moduleResolution": "node16", |
Prompt To Fix With AI
This is a comment left during a code review.
Path: samples/typescript/tsconfig.json
Line: 5:5
Comment:
`moduleResolution: "bundler"` is intended for bundlers like Vite/esbuild. For Node.js with `tsx`, use `"node"` or `"node16"`:
```suggestion
"moduleResolution": "node16",
```
How can I resolve this? If you propose a fix, please make it concise.ed33d3f to
792b3b2
Compare
f0dbb39 to
62f02be
Compare
Express server mirroring the Kotlin sample with the same API contract. Uses @lightsparkdev/grid SDK, tsx runtime, and serves the shared React frontend. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
62f02be to
bb08f69
Compare
| import express from "express"; | ||
| import path from "node:path"; | ||
| import { fileURLToPath } from "node:url"; | ||
| import { config } from "./config.js"; |
There was a problem hiding this comment.
Unused import
config is imported but never referenced in the body of index.ts. The env var validation side effect still runs through the transitive dependency chain (route imports → gridClient.ts → config.ts), so this import is redundant.
| import { config } from "./config.js"; | |
| import { log } from "./log.js"; |
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Prompt To Fix With AI
This is a comment left during a code review.
Path: samples/typescript/src/index.ts
Line: 4:4
Comment:
**Unused import**
`config` is imported but never referenced in the body of `index.ts`. The env var validation side effect still runs through the transitive dependency chain (route imports → `gridClient.ts` → `config.ts`), so this import is redundant.
```suggestion
import { log } from "./log.js";
```
<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>
How can I resolve this? If you propose a fix, please make it concise.| res.header("Access-Control-Allow-Origin", "*"); | ||
| res.header("Access-Control-Allow-Methods", "GET, POST, OPTIONS"); | ||
| res.header("Access-Control-Allow-Headers", "Content-Type, Authorization"); | ||
| res.header("Access-Control-Allow-Credentials", "true"); |
There was a problem hiding this comment.
Invalid CORS header combination
Setting Access-Control-Allow-Credentials: true together with Access-Control-Allow-Origin: * is invalid per the CORS spec. Browsers will reject credentialed cross-origin requests with this configuration. Since this sample doesn't rely on credentialed cross-origin requests (the Vite dev proxy handles it), the simplest fix is to remove the credentials header:
| res.header("Access-Control-Allow-Origin", "*"); | |
| res.header("Access-Control-Allow-Methods", "GET, POST, OPTIONS"); | |
| res.header("Access-Control-Allow-Headers", "Content-Type, Authorization"); | |
| res.header("Access-Control-Allow-Credentials", "true"); | |
| res.header("Access-Control-Allow-Origin", "*"); | |
| res.header("Access-Control-Allow-Methods", "GET, POST, OPTIONS"); | |
| res.header("Access-Control-Allow-Headers", "Content-Type, Authorization"); |
Note: The Kotlin sample uses Ktor's CORS plugin which handles this correctly by reflecting the request Origin header instead of sending * when credentials are enabled.
Prompt To Fix With AI
This is a comment left during a code review.
Path: samples/typescript/src/index.ts
Line: 20:23
Comment:
**Invalid CORS header combination**
Setting `Access-Control-Allow-Credentials: true` together with `Access-Control-Allow-Origin: *` is [invalid per the CORS spec](https://fetch.spec.whatwg.org/#http-access-control-allow-credentials). Browsers will reject credentialed cross-origin requests with this configuration. Since this sample doesn't rely on credentialed cross-origin requests (the Vite dev proxy handles it), the simplest fix is to remove the credentials header:
```suggestion
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
res.header("Access-Control-Allow-Headers", "Content-Type, Authorization");
```
Note: The Kotlin sample uses Ktor's CORS plugin which handles this correctly by reflecting the request `Origin` header instead of sending `*` when credentials are enabled.
How can I resolve this? If you propose a fix, please make it concise.| "dev": "tsx src/index.ts" | ||
| }, | ||
| "dependencies": { | ||
| "@lightsparkdev/grid": "latest", |
There was a problem hiding this comment.
Pin SDK version for reproducibility
Using "latest" means a fresh npm install (without an existing lockfile) will pull whatever the newest version is, which could include breaking changes. Since the lockfile pins 0.5.0, consider pinning to "^0.5.0" for predictable behavior:
| "@lightsparkdev/grid": "latest", | |
| "@lightsparkdev/grid": "^0.5.0", |
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Prompt To Fix With AI
This is a comment left during a code review.
Path: samples/typescript/package.json
Line: 12:12
Comment:
**Pin SDK version for reproducibility**
Using `"latest"` means a fresh `npm install` (without an existing lockfile) will pull whatever the newest version is, which could include breaking changes. Since the lockfile pins `0.5.0`, consider pinning to `"^0.5.0"` for predictable behavior:
```suggestion
"@lightsparkdev/grid": "^0.5.0",
```
<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>
How can I resolve this? If you propose a fix, please make it concise.
Add TypeScript backend sample for Grid API
Add a TypeScript (Express) backend implementation that mirrors the existing Kotlin sample with the same API contract. This implementation:
@lightsparkdev/gridTypeScript SDK to interact with the Grid API/publicdirectoryThe implementation uses modern ES modules, the tsx runtime for TypeScript execution, and proper error handling throughout.
Co-Authored-By: Claude Opus 4.6 noreply@anthropic.com