Skip to content

Commit 285eca1

Browse files
Update edge endpoint
Signed-off-by: Andy Kwok <[email protected]>
1 parent 0b87776 commit 285eca1

File tree

2 files changed

+186
-2
lines changed

2 files changed

+186
-2
lines changed

solutions/aws-neptune-analytics/README.md

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ deployUrl: https://vercel.com/new/clone?repository-url=https://github.com/vercel
88

99
# Next.js + AWS Neptune Analytics
1010

11-
This is an example of a Next.js application using AWS Neptune Analytics for creating, reading, updating, and deleting graph nodes with OpenCypher queries.
11+
This is an example of a Next.js application using AWS Neptune Analytics for creating, reading, updating, and deleting graph nodes and edges with OpenCypher queries.
1212

1313
## How to Use
1414

@@ -55,13 +55,22 @@ const client = new NeptuneGraphClient({})
5555

5656
## API Endpoints
5757

58-
The application provides a RESTful API for graph node operations:
58+
The application provides a RESTful API for graph node and edge operations:
59+
60+
### Node Operations
5961

6062
- `GET /api/node?id={id}` - Retrieve a node by ID
6163
- `POST /api/node` - Create a new node
6264
- `PUT /api/node` - Update an existing node
6365
- `DELETE /api/node?id={id}` - Delete a node and its relationships
6466

67+
### Edge Operations
68+
69+
- `GET /api/edge?id={id}` - Retrieve an edge by ID
70+
- `POST /api/edge` - Create a new edge
71+
- `PUT /api/edge` - Update an existing edge
72+
- `DELETE /api/edge?id={id}` - Delete an edge
73+
6574
## Testing
6675

6776
### Create Node (POST)
@@ -91,3 +100,31 @@ curl -X PUT http://localhost:3000/api/node \
91100
```bash
92101
curl -X DELETE "http://localhost:3000/api/node?id=user-123"
93102
```
103+
104+
### Create Edge (POST)
105+
106+
```bash
107+
curl -X POST http://localhost:3000/api/edge \
108+
-d '{"fromId": "user-123", "toId": "user-456", "type": "FOLLOWS"}' \
109+
-H "Content-type: application/json"
110+
```
111+
112+
### Get Edge (GET)
113+
114+
```bash
115+
curl "http://localhost:3000/api/edge?id=follows-001"
116+
```
117+
118+
### Update Edge (PUT)
119+
120+
```bash
121+
curl -X PUT http://localhost:3000/api/edge \
122+
-d '{"id": "follows-001", "since": "2024-01-15", "strength": "strong"}' \
123+
-H "Content-type: application/json"
124+
```
125+
126+
### Delete Edge (DELETE)
127+
128+
```bash
129+
curl -X DELETE "http://localhost:3000/api/edge?id=follows-001"
130+
```
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
import { NextResponse } from 'next/server'
2+
import * as NG from '@aws-sdk/client-neptune-graph'
3+
4+
const client = new NG.NeptuneGraphClient({})
5+
const GRAPH_ID = process.env.GRAPH_ID
6+
7+
if (!GRAPH_ID) {
8+
throw new Error('GRAPH_ID environment variable is required')
9+
}
10+
11+
/**
12+
* Execute Neptune Analytics query with parameters
13+
*/
14+
async function executeQuery(
15+
queryString: string,
16+
parameters: Record<string, any>
17+
) {
18+
const input = {
19+
graphIdentifier: GRAPH_ID,
20+
queryString,
21+
language: NG.QueryLanguage.OPEN_CYPHER,
22+
parameters,
23+
}
24+
25+
const cmd = new NG.ExecuteQueryCommand(input)
26+
const response = await client.send(cmd)
27+
const responseStr = await response.payload.transformToString()
28+
return JSON.parse(responseStr)
29+
}
30+
31+
/**
32+
* Handle errors with consistent logging and response format
33+
*/
34+
function handleError(error: any, method: string) {
35+
console.error(`${method} /api/edge error:`, error)
36+
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
37+
}
38+
39+
/**
40+
* Validate ID parameter from query string
41+
*/
42+
function validateId(id: string | null) {
43+
if (!id) {
44+
return NextResponse.json(
45+
{ error: 'id parameter is required' },
46+
{ status: 400 }
47+
)
48+
}
49+
return null
50+
}
51+
52+
/**
53+
* Validate request body contains required fields for edge creation
54+
*/
55+
function validateEdgeBody(body: any) {
56+
if (!body?.fromId || !body?.toId || !body?.type) {
57+
return NextResponse.json(
58+
{ error: 'Request body with fromId, toId, and type is required' },
59+
{ status: 400 }
60+
)
61+
}
62+
return null
63+
}
64+
65+
export async function GET(request: Request) {
66+
try {
67+
const { searchParams } = new URL(request.url)
68+
const id = searchParams.get('id')
69+
70+
const error = validateId(id)
71+
if (error) return error
72+
73+
const result = await executeQuery(
74+
'MATCH()-[r]->() WHERE id(r) = $EDGE_ID RETURN r',
75+
{ EDGE_ID: id }
76+
)
77+
78+
return NextResponse.json(result)
79+
} catch (error) {
80+
return handleError(error, 'GET')
81+
}
82+
}
83+
84+
export async function POST(request: Request) {
85+
try {
86+
const body = await request.json()
87+
88+
const error = validateEdgeBody(body)
89+
if (error) return error
90+
91+
const { fromId, toId, type, ...properties } = body
92+
93+
const result = await executeQuery(
94+
'MATCH (from), (to) WHERE id(from) = $FROM_ID AND id(to) = $TO_ID CREATE (from)-[r:' +
95+
type +
96+
']->(to) SET r += $PROPERTIES RETURN r',
97+
{ FROM_ID: fromId, TO_ID: toId, PROPERTIES: properties }
98+
)
99+
100+
return NextResponse.json(result, { status: 201 })
101+
} catch (error) {
102+
return handleError(error, 'POST')
103+
}
104+
}
105+
106+
export async function PUT(request: Request) {
107+
try {
108+
const body = await request.json()
109+
110+
if (!body?.id) {
111+
return NextResponse.json(
112+
{ error: 'Request body with id is required' },
113+
{ status: 400 }
114+
)
115+
}
116+
117+
const { id, ...properties } = body
118+
119+
const result = await executeQuery(
120+
'MATCH()-[r]->() WHERE id(r) = $EDGE_ID SET r = $PROPERTIES RETURN r',
121+
{ EDGE_ID: id, PROPERTIES: properties }
122+
)
123+
124+
return NextResponse.json(result)
125+
} catch (error) {
126+
return handleError(error, 'PUT')
127+
}
128+
}
129+
130+
export async function DELETE(request: Request) {
131+
try {
132+
const { searchParams } = new URL(request.url)
133+
const id = searchParams.get('id')
134+
135+
const error = validateId(id)
136+
if (error) return error
137+
138+
const result = await executeQuery(
139+
'MATCH()-[r]->() WHERE id(r) = $EDGE_ID DELETE r',
140+
{ EDGE_ID: id }
141+
)
142+
143+
return NextResponse.json(result)
144+
} catch (error) {
145+
return handleError(error, 'DELETE')
146+
}
147+
}

0 commit comments

Comments
 (0)