Skip to content

feat(samples): add TypeScript backend using Grid TypeScript SDK#197

Open
pengying wants to merge 1 commit intomainfrom
02-12-feat_adding_typescript_sample
Open

feat(samples): add TypeScript backend using Grid TypeScript SDK#197
pengying wants to merge 1 commit intomainfrom
02-12-feat_adding_typescript_sample

Conversation

@pengying
Copy link
Contributor

@pengying pengying commented Feb 13, 2026

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:

  • Uses the @lightsparkdev/grid TypeScript SDK to interact with the Grid API
  • Provides the same API endpoints for customers, external accounts, quotes, and sandbox operations
  • Streams webhook events to the frontend via Server-Sent Events (SSE)
  • Serves the shared React frontend from the /public directory
  • Includes comprehensive documentation and setup instructions

The 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

Copy link
Contributor Author

pengying commented Feb 13, 2026

@pengying pengying marked this pull request as ready for review February 13, 2026 22:28
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 13, 2026

Greptile Summary

Adds 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 @lightsparkdev/grid TypeScript SDK and serves the shared React frontend from a public/ directory.

  • All five API endpoints and SSE streaming are implemented and match the Kotlin sample's contract
  • Webhook signature verification is intentionally skipped (with a TODO) since the TypeScript SDK doesn't yet expose the unwrap method
  • Minor issues: unused config import in index.ts, invalid CORS header combination (Allow-Credentials: true with Allow-Origin: *), and "latest" version specifier for the Grid SDK in package.json
  • Documentation is thorough with setup instructions, architecture diagram, and dev workflow options

Confidence Score: 4/5

  • This PR is safe to merge — it adds a new self-contained sample directory with no impact on existing code beyond minor README and gitignore updates.
  • The new TypeScript sample correctly implements the documented API contract and mirrors the Kotlin sample's functionality. Issues found are minor (unused import, CORS header combination, unpinned SDK version) and do not affect correctness for the sample's intended use case. No existing code is modified beyond documentation updates.
  • samples/typescript/src/index.ts has an unused import and an invalid CORS header combination. samples/typescript/package.json uses "latest" for the Grid SDK dependency.

Important Files Changed

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)
Loading

Last reviewed commit: bb08f69

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

17 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moduleResolution: "bundler" is intended for bundlers like Vite/esbuild. For Node.js with tsx, use "node" or "node16":

Suggested change
"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.

@pengying pengying changed the base branch from 02-12-feat_adding_kotlin_sample to graphite-base/197 February 18, 2026 18:49
@pengying pengying force-pushed the 02-12-feat_adding_typescript_sample branch from f0dbb39 to 62f02be Compare February 18, 2026 18:49
@graphite-app graphite-app bot changed the base branch from graphite-base/197 to main February 18, 2026 18:50
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>
@pengying pengying force-pushed the 02-12-feat_adding_typescript_sample branch from 62f02be to bb08f69 Compare February 18, 2026 18:50
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

17 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

import express from "express";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { config } from "./config.js";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.tsconfig.ts), so this import is redundant.

Suggested change
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.

Comment on lines +20 to +23
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");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

Suggested change
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",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

Suggested change
"@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments