Skip to content

Commit af30728

Browse files
authored
Merge branch 'main' into update-modus-overview
2 parents e9c4c6e + 39ef32c commit af30728

File tree

6 files changed

+501
-38
lines changed

6 files changed

+501
-38
lines changed

.github/workflows/no-broken-links.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
- name: Setup Node.js
1919
uses: actions/setup-node@v4
2020
with:
21-
node-version: 22.15.1
21+
node-version: 22.16.0
2222

2323
- name: Run Mintlify Script
2424
run: npx mintlify broken-links

.github/workflows/trunk.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
name: Trunk Code Quality
2+
on:
3+
workflow_dispatch:
4+
pull_request:
5+
branches: main
6+
7+
permissions:
8+
contents: read
9+
10+
jobs:
11+
call-workflow-in-another-repo:
12+
uses: hypermodeinc/.github/.github/workflows/trunk.yml@main

dgraph/sdks/javascript.mdx

Lines changed: 303 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,3 +424,306 @@ meta.add("auth-token", "mySuperSecret")
424424

425425
await dgraphClient.alter(op, meta)
426426
```
427+
428+
## Browser support
429+
430+
<Note>
431+
The official Dgraph JavaScript gRPC client is designed for Node.js
432+
environments and doesn't officially support browser usage due to gRPC-web
433+
limitations and bundling complexities. However, you can achieve **most** of
434+
the same capabilities in browsers using Dgraph's HTTP API with standard
435+
`fetch` requests.
436+
</Note>
437+
438+
<Tip>
439+
If you only need basic CRUD operations and don't require admin endpoints (like
440+
schema alterations or dropping data), consider using Dgraph's GraphQL API
441+
instead, which is designed for client-side usage and provides better security
442+
boundaries.
443+
</Tip>
444+
445+
<Warning>
446+
We don't recommend connecting directly to Dgraph from browser applications as
447+
this could expose your database credentials and connection details to end
448+
users. Consider using a backend API as a proxy instead.
449+
</Warning>
450+
451+
### Node.js gRPC vs browser HTTP API
452+
453+
Below are side-by-side examples showing how to perform common operations using
454+
both the Node.js gRPC client and browser-compatible HTTP requests.
455+
456+
#### Creating a connection
457+
458+
**Node.js (gRPC):**
459+
460+
```js
461+
const dgraph = require("dgraph-js")
462+
const grpc = require("grpc")
463+
464+
const clientStub = new dgraph.DgraphClientStub(
465+
"localhost:9080",
466+
grpc.credentials.createInsecure(),
467+
)
468+
const dgraphClient = new dgraph.DgraphClient(clientStub)
469+
```
470+
471+
**Browser (HTTP):**
472+
473+
```js
474+
const DGRAPH_HTTP_URL = "http://localhost:8080"
475+
476+
// Test connection to Dgraph
477+
const response = await fetch(`${DGRAPH_HTTP_URL}/health`)
478+
if (!response.ok) {
479+
throw new Error(`Cannot connect to Dgraph: ${response.status}`)
480+
}
481+
console.log("Connected to Dgraph successfully")
482+
```
483+
484+
#### Setting schema
485+
486+
**Node.js (gRPC):**
487+
488+
```js
489+
const schema = "name: string @index(exact) ."
490+
const op = new dgraph.Operation()
491+
op.setSchema(schema)
492+
await dgraphClient.alter(op)
493+
```
494+
495+
**Browser (HTTP):**
496+
497+
```js
498+
const schema = "name: string @index(exact) ."
499+
500+
await fetch(`${DGRAPH_HTTP_URL}/alter`, {
501+
method: "POST",
502+
headers: { "Content-Type": "application/rdf" },
503+
body: schema,
504+
})
505+
```
506+
507+
#### Running queries
508+
509+
**Node.js (gRPC):**
510+
511+
```js
512+
const query = `query all($a: string) {
513+
all(func: eq(name, $a)) {
514+
name
515+
}
516+
}`
517+
const vars = { $a: "Alice" }
518+
const res = await dgraphClient.newTxn().queryWithVars(query, vars)
519+
const data = res.getJson()
520+
```
521+
522+
**Browser (HTTP):**
523+
524+
```js
525+
const query = `query all($a: string) {
526+
all(func: eq(name, $a)) {
527+
name
528+
}
529+
}`
530+
const vars = { $a: "Alice" }
531+
532+
const response = await fetch(`${DGRAPH_HTTP_URL}/query`, {
533+
method: "POST",
534+
headers: { "Content-Type": "application/json" },
535+
body: JSON.stringify({ query, variables: vars }),
536+
})
537+
const data = await response.json()
538+
```
539+
540+
#### Running mutations
541+
542+
**Node.js (gRPC):**
543+
544+
```js
545+
const txn = dgraphClient.newTxn()
546+
try {
547+
const p = { name: "Alice", age: 26 }
548+
const mu = new dgraph.Mutation()
549+
mu.setSetJson(p)
550+
mu.setCommitNow(true)
551+
await txn.mutate(mu)
552+
} finally {
553+
await txn.discard()
554+
}
555+
```
556+
557+
**Browser (HTTP):**
558+
559+
```js
560+
const p = { name: "Alice", age: 26 }
561+
562+
await fetch(`${DGRAPH_HTTP_URL}/mutate?commitNow=true`, {
563+
method: "POST",
564+
headers: { "Content-Type": "application/json" },
565+
body: JSON.stringify({ set: [p] }),
566+
})
567+
```
568+
569+
#### Upsert operations
570+
571+
**Node.js (gRPC):**
572+
573+
```js
574+
const query = `query {
575+
user as var(func: eq(email, "[email protected]"))
576+
}`
577+
578+
const mu = new dgraph.Mutation()
579+
mu.setSetNquads(`uid(user) <email> "[email protected]" .`)
580+
581+
const req = new dgraph.Request()
582+
req.setQuery(query)
583+
req.setMutationsList([mu])
584+
req.setCommitNow(true)
585+
586+
await dgraphClient.newTxn().doRequest(req)
587+
```
588+
589+
**Browser (HTTP):**
590+
591+
```js
592+
const query = `query {
593+
user as var(func: eq(email, "[email protected]"))
594+
}`
595+
596+
await fetch(`${DGRAPH_HTTP_URL}/mutate?commitNow=true`, {
597+
method: "POST",
598+
headers: { "Content-Type": "application/json" },
599+
body: JSON.stringify({
600+
query: query,
601+
set: [{ uid: "uid(user)", email: "[email protected]" }],
602+
}),
603+
})
604+
```
605+
606+
#### Conditional upserts
607+
608+
**Node.js (gRPC):**
609+
610+
```js
611+
const query = `query {
612+
user as var(func: eq(email, "[email protected]"))
613+
}`
614+
615+
const mu = new dgraph.Mutation()
616+
mu.setSetNquads(`uid(user) <email> "[email protected]" .`)
617+
mu.setCond(`@if(eq(len(user), 1))`)
618+
619+
const req = new dgraph.Request()
620+
req.setQuery(query)
621+
req.addMutations(mu)
622+
req.setCommitNow(true)
623+
624+
await dgraphClient.newTxn().doRequest(req)
625+
```
626+
627+
**Browser (HTTP):**
628+
629+
```js
630+
const query = `query {
631+
user as var(func: eq(email, "[email protected]"))
632+
}`
633+
634+
await fetch(`${DGRAPH_HTTP_URL}/mutate?commitNow=true`, {
635+
method: "POST",
636+
headers: { "Content-Type": "application/json" },
637+
body: JSON.stringify({
638+
query: query,
639+
mutations: [
640+
{
641+
set: [{ uid: "uid(user)", email: "[email protected]" }],
642+
cond: "@if(eq(len(user), 1))",
643+
},
644+
],
645+
}),
646+
})
647+
```
648+
649+
#### Drop all data
650+
651+
**Node.js (gRPC):**
652+
653+
```js
654+
const op = new dgraph.Operation()
655+
op.setDropAll(true)
656+
await dgraphClient.alter(op)
657+
```
658+
659+
**Browser (HTTP):**
660+
661+
```js
662+
await fetch(`${DGRAPH_HTTP_URL}/alter`, {
663+
method: "POST",
664+
headers: { "Content-Type": "application/json" },
665+
body: JSON.stringify({ drop_all: true }),
666+
})
667+
```
668+
669+
#### Authentication
670+
671+
**Node.js (gRPC):**
672+
673+
```js
674+
const meta = new grpc.Metadata()
675+
meta.add("auth-token", "mySuperSecret")
676+
await dgraphClient.alter(op, meta)
677+
```
678+
679+
**Browser (HTTP):**
680+
681+
```js
682+
await fetch(`${DGRAPH_HTTP_URL}/alter`, {
683+
method: "POST",
684+
headers: {
685+
"Content-Type": "application/rdf",
686+
"auth-token": "mySuperSecret",
687+
},
688+
body: schema,
689+
})
690+
```
691+
692+
### Browser-specific considerations
693+
694+
#### Connection strings
695+
696+
For convenience, you can parse connection strings similar to database URLs:
697+
698+
```js
699+
function parseDgraphUrl(connectionString) {
700+
if (connectionString.startsWith("http")) {
701+
return { url: connectionString, headers: {} }
702+
}
703+
704+
// Handle dgraph://user:pass@host:port format
705+
const url = new URL(connectionString.replace("dgraph://", "https://"))
706+
const headers = {}
707+
708+
if (url.username && url.password) {
709+
headers["Authorization"] =
710+
`Basic ${btoa(`${url.username}:${url.password}`)}`
711+
}
712+
713+
return {
714+
url: `http://${url.hostname}:${url.port || 8080}`,
715+
headers,
716+
}
717+
}
718+
719+
// Usage
720+
const { url, headers } = parseDgraphUrl("dgraph://user:pass@localhost:8080")
721+
const DGRAPH_HTTP_URL = url
722+
```
723+
724+
### Limitations of HTTP API
725+
726+
- **No streaming**: HTTP doesn't support gRPC streaming capabilities
727+
- **Transaction isolation**: HTTP requests are stateless; use upserts for
728+
consistency
729+
- **Performance**: Higher overhead compared to persistent gRPC connections

model-router.mdx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,8 @@ models.
677677
678678
</Note>
679679

680+
### Model Introspection
681+
680682
The full list of available models is available via the API.
681683

682684
```bash curl
@@ -694,6 +696,8 @@ Large language models provide text generation and reasoning capabilities.
694696

695697
| Provider | Model | Slug |
696698
| --------- | ----------------------------------- | ------------------------------------------- |
699+
| Anthropic | Claude 4 Sonnet | `claude-sonnet-4-20250514` |
700+
| Anthropic | Claude 4 Opus | `claude-opus-4-20250514` |
697701
| Anthropic | Claude 3.7 Sonnet (latest) | `claude-3-7-sonnet-latest` |
698702
| Anthropic | Claude 3.7 Sonnet | `claude-3-7-sonnet-20250219` |
699703
| Anthropic | Claude 3.5 Sonnet (latest) | `claude-3-5-sonnet-latest` |
@@ -705,7 +709,7 @@ Large language models provide text generation and reasoning capabilities.
705709
| Anthropic | Claude 3 Opus | `claude-3-opus-20240229` |
706710
| Anthropic | Claude 3 Sonnet | `claude-3-sonnet-20240229` |
707711
| Anthropic | Claude 3 Haiku | `claude-3-haiku-20240307` |
708-
| DeepSeek | DeepSeek-R1-Distill-Llama | `deepseek-ai/deepseek-r1-distill-llama-8b` |
712+
| DeepSeek | DeepSeek-R1-Distill-Llama | `deepseek-ai/deepseek-r1-distill-llama-70b` |
709713
| Google | Gemini 2.5 Pro | `gemini-2.5-pro-exp-03-25` |
710714
| Google | Gemini 2.5 Pro Preview | `gemini-2.5-pro-preview-05-06` |
711715
| Google | Gemini 2.5 Flash Preview | `gemini-2.5-flash-preview-04-17` |
@@ -728,7 +732,7 @@ Large language models provide text generation and reasoning capabilities.
728732
| Google | Gemini 1.5 Flash 8B | `gemini-1.5-flash-8b-exp-0827` |
729733
| Google | Gemini 1.5 Flash 8B | `gemini-1.5-flash-8b-001` |
730734
| Meta | Llama 4 Scout | `meta-llama/llama-4-scout-17b-16e-instruct` |
731-
| Meta | Llama 3.2 | `meta-llama/llama-3.2-3b-instruct` |
735+
| Meta | Llama 3.3 | `meta-llama/llama-3.3-70b-versatile` |
732736
| OpenAI | O3 (latest) | `o3` |
733737
| OpenAI | O3 | `o3-2025-04-16` |
734738
| OpenAI | O4 Mini (latest) | `o4-mini` |

modus/api-generation.mdx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,3 +138,29 @@ function classify(text: string, threshold: f32): ClassifierResult[] {
138138

139139
</Tab>
140140
</Tabs>
141+
142+
## Generating mutations
143+
144+
By default, all exported functions are generated as GraphQL **queries** unless
145+
they follow specific naming conventions that indicate they perform mutations
146+
(data modifications).
147+
148+
Functions are automatically classified as **mutations** when they start with
149+
these prefixes:
150+
151+
- `mutate`
152+
- `post`, `patch`, `put`, `delete`
153+
- `add`, `update`, `insert`, `upsert`
154+
- `create`, `edit`, `save`, `remove`, `alter`, `modify`
155+
156+
For example:
157+
158+
- `getUserById` → Query
159+
- `listProducts` → Query
160+
- `addUser` → Mutation
161+
- `updateProduct` → Mutation
162+
- `deleteOrder` → Mutation
163+
164+
The prefix is detected precisely - `addPost` becomes a mutation, but
165+
`additionalPosts` remains a query since "additional" doesn't match the exact
166+
"add" prefix pattern.

0 commit comments

Comments
 (0)