import{r as o,p as d,q as h,w as n,z as t,t as a,R as e}from"./vendor.3d5b4206.js";const c={class:"markdown-body"},u=t("blockquote",null,[t("p",null,"Directus offers both a RESTful and GraphQL API to manage the data in the database. The API has predictable resource-oriented URLs, relies on standard HTTP status codes, and uses JSON for input and output.")],-1),p=t("p",null,[t("div",{class:"table-of-contents"},[t("ul",null,[t("li",null,[t("a",{href:"#dynamic-api"},"Dynamic API")]),t("li",null,[t("a",{href:"#rest-vs.-graphql"},"REST vs. GraphQL")]),t("li",null,[t("a",{href:"#authentication"},"Authentication")]),t("li",null,[t("a",{href:"#relational-data"},"Relational Data")]),t("li",null,[t("a",{href:"#search-http-method"},"SEARCH HTTP Method")]),t("li",null,[t("a",{href:"#system-data-in-graphql"},"System data in GraphQL")]),t("li",null,[t("a",{href:"#error-codes"},"Error Codes")])])])],-1),_=t("h2",{id:"dynamic-api"},[e("Dynamic API "),t("a",{class:"header-anchor",href:"#dynamic-api"},"#")],-1),f=e("The platform\u2019s API uses "),m=e("Database Mirroring"),y=e(" to dynamically generate REST endpoints and a GraphQL schema based on the connected database\u2019s architecture. Since these endpoints return data based on your specific schema and configured permissions, the input/output of the API differs greatly for individual installations."),g=t("h2",{id:"rest-vs.-graphql"},[e("REST vs. GraphQL "),t("a",{class:"header-anchor",href:"#rest-vs.-graphql"},"#")],-1),b=t("p",null,[t("strong",null,"There is no difference in the functionality available between the REST and GraphQL endpoints."),e(" The functionality available in both is mapped to same set of core services, meaning that you don\u2019t lose any performance or capabilities by choosing one or the other.")],-1),w=t("p",null,"Which one you choose is ultimately up to you.",-1),T=t("h2",{id:"authentication"},[e("Authentication "),t("a",{class:"header-anchor",href:"#authentication"},"#")],-1),j=e("By default, all data in the system is off-limits for unauthenticated users. To gain access to protected data, you must "),v=e("include an access token with every request"),E=e(", or "),A=e("configure permissions for the public role"),k=e("."),I=t("p",null,"Useful references:",-1),D=e("Authenticating into the API"),P=e("Login endpoint reference"),S=t("h2",{id:"relational-data"},[e("Relational Data "),t("a",{class:"header-anchor",href:"#relational-data"},"#")],-1),q=e("Directus only retrieves the fields in your items that explicitly have been requested. Relational data can be retrieved nested by using "),R=e("the "),L=t("code",null,"fields",-1),x=e(" parameter"),C=e(" in REST, or regular nested queries in GraphQL. This allows you to retrieve the author of your article included in the articles data, or fetch related log entry points for your app\u2019s analytics data for example."),H=t("h3",{id:"creating-%2F-updating-%2F-deleting"},[e("Creating / Updating / Deleting "),t("a",{class:"header-anchor",href:"#creating-%2F-updating-%2F-deleting"},"#")],-1),O=t("p",null,"Similarly to fetching, relational content can be modified deeply as well.",-1),N=t("h4",{id:"many-to-one"},[e("Many-to-One "),t("a",{class:"header-anchor",href:"#many-to-one"},"#")],-1),M=t("p",null,"Many-to-One relationships are fairly straightforward to manage relationally. You can simply submit the changes you want as an object under the relational key in your collection. For example, if you wanted to create a new featured article on your page, you could submit:",-1),U=t("pre",null,[t("code",{class:"language-json"},[e(`{
	`),t("span",{class:"hljs-attr"},'"featured_article"'),e(`: {
		`),t("span",{class:"hljs-attr"},'"title"'),e(": "),t("span",{class:"hljs-string"},'"This is my new article!"'),e(`
	}
}
`)])],-1),G=t("p",null,[e("This will create a new record in the related collection, and save its primary key in the "),t("code",null,"featured_article"),e(" field for this item. To update an existing item, simply provide the primary key with the updates, and Directus will treat it as an update instead of a creation:")],-1),F=t("pre",null,[t("code",{class:"language-json"},[e(`{
	`),t("span",{class:"hljs-attr"},'"featured_article"'),e(`: {
		`),t("span",{class:"hljs-attr"},'"id"'),e(": "),t("span",{class:"hljs-number"},"15"),e(`,
		`),t("span",{class:"hljs-attr"},'"title"'),e(": "),t("span",{class:"hljs-string"},'"This is an updated title for my article!"'),e(`
	}
}
`)])],-1),Q=t("p",null,"Seeing that the Many-to-One relationship stores the foreign key on the field itself, removing the item can be done by nullifying the field:",-1),V=t("pre",null,[t("code",{class:"language-json"},[e(`{
	`),t("span",{class:"hljs-attr"},'"featured_article"'),e(": "),t("span",{class:"hljs-literal"},"null"),e(`
}
`)])],-1),B=t("h4",{id:"one-to-many-(%2F-many-to-many)"},[e("One-to-Many (/ Many-to-Many) "),t("a",{class:"header-anchor",href:"#one-to-many-(%2F-many-to-many)"},"#")],-1),Y=t("p",null,"One-to-Many, and therefore Many-to-Many and Many-to-Any, relationships can be updated in one of two ways:",-1),W=t("p",null,[t("strong",null,"Basic")],-1),z=t("p",null,[e("The API will return one-to-many fields as an array of nested keys or items (based on the "),t("code",null,"fields"),e(" parameter). You can use this same structure to select what the related items are:")],-1),J=t("pre",null,[t("code",{class:"language-json"},[e(`{
	`),t("span",{class:"hljs-attr"},'"children"'),e(": ["),t("span",{class:"hljs-number"},"2"),e(", "),t("span",{class:"hljs-number"},"7"),e(", "),t("span",{class:"hljs-number"},"149"),e(`]
}
`)])],-1),X=t("p",null,"You can also provide an object instead of a primary key in order to create new items nested on the fly, or an object with a primary key included to update an existing item:",-1),K=t("pre",null,[t("code",{class:"language-json"},[e(`{
	`),t("span",{class:"hljs-attr"},'"children"'),e(`: [
		`),t("span",{class:"hljs-number"},"2"),e(", "),t("span",{class:"hljs-comment"},"// assign existing item 2 to be a child of the current item"),e(`
		{
			`),t("span",{class:"hljs-attr"},'"name"'),e(": "),t("span",{class:"hljs-string"},'"A new nested item"'),e(`
		},
		{
			`),t("span",{class:"hljs-attr"},'"id"'),e(": "),t("span",{class:"hljs-number"},"149"),e(`,
			`),t("span",{class:"hljs-attr"},'"name"'),e(": "),t("span",{class:"hljs-string"},'"Assign and update existing item 149"'),e(`
		}
	]
}
`)])],-1),Z=t("p",null,"To remove items from this relationship, simply omit them from the array:",-1),$=t("pre",null,[t("code",{class:"language-json"},[e(`{
	`),t("span",{class:"hljs-attr"},'"children"'),e(": ["),t("span",{class:"hljs-number"},"2"),e(", "),t("span",{class:"hljs-number"},"149"),e(`]
}
`)])],-1),tt=t("p",null,"This method of updating a one-to-many is very useful for smaller relational datasets.",-1),et=t("p",null,[t("strong",null,"\u201CDetailed\u201D")],-1),nt=t("p",null,"Alternatively, you can provide an object detailing the changes as follows:",-1),st=t("pre",null,[t("code",{class:"language-json"},[e(`{
	`),t("span",{class:"hljs-attr"},'"children"'),e(`: {
		`),t("span",{class:"hljs-attr"},'"create"'),e(": [{ "),t("span",{class:"hljs-attr"},'"name"'),e(": "),t("span",{class:"hljs-string"},'"A new nested item"'),e(` }],
		`),t("span",{class:"hljs-attr"},'"update"'),e(": [{ "),t("span",{class:"hljs-attr"},'"id"'),e(": "),t("span",{class:"hljs-number"},"149"),e(", "),t("span",{class:"hljs-attr"},'"name"'),e(": "),t("span",{class:"hljs-string"},'"A new nested item"'),e(` }],
		`),t("span",{class:"hljs-attr"},'"delete"'),e(": ["),t("span",{class:"hljs-number"},"7"),e(`]
	}
}
`)])],-1),at=t("p",null,"This is useful if you need to have more tightly control on staged changes, or when you\u2019re working with a big relational dataset.",-1),lt=t("h4",{id:"many-to-any-(union-types)"},[e("Many-to-Any (Union Types) "),t("a",{class:"header-anchor",href:"#many-to-any-(union-types)"},"#")],-1),ot=t("p",null,"Many-to-Any fields work very similar to a \u201Cregular\u201D many-to-many, with the exception that the related field can pull in the fields from any of the related collections, for example:",-1),rt=t("pre",null,[t("code",{class:"language-json"},[e(`{
	`),t("span",{class:"hljs-attr"},'"sections"'),e(`: [
		{
			`),t("span",{class:"hljs-attr"},'"collection"'),e(": "),t("span",{class:"hljs-string"},'"headings"'),e(`,
			`),t("span",{class:"hljs-attr"},'"item"'),e(`: {
				`),t("span",{class:"hljs-comment"},"/* headings fields */"),e(`
			}
		},
		{
			`),t("span",{class:"hljs-attr"},'"collection"'),e(": "),t("span",{class:"hljs-string"},'"paragraphs"'),e(`,
			`),t("span",{class:"hljs-attr"},'"item"'),e(`: {
				`),t("span",{class:"hljs-comment"},"/* paragraphs fields */"),e(`
			}
		}
	]
}
`)])],-1),it=t("h5",{id:"rest-api"},[e("REST API "),t("a",{class:"header-anchor",href:"#rest-api"},"#")],-1),dt=t("p",null,[e("To scope the fields that are returned per collection type, you can use the "),t("code",null,"<field>:<scope>"),e(" syntax in the fields parameter as follows:")],-1),ht=t("pre",null,[t("code",null,`GET /items/pages
	?fields[]=sections.item:headings.id
	&fields[]=sections.item:headings.title
	&fields[]=sections.item:paragraphs.body
	&fields[]=sections.item:paragraphs.background_color
`)],-1),ct=t("h5",{id:"graphql"},[e("GraphQL "),t("a",{class:"header-anchor",href:"#graphql"},"#")],-1),ut=t("p",null,"In GraphQL, you can use nested fragments on the Union Type to select the fields:",-1),pt=t("pre",null,[t("code",{class:"language-graphql"},[t("span",{class:"hljs-keyword"},"query"),e(` {
	pages {
		sections {
			item {
				`),t("span",{class:"hljs-keyword"},"..."),e(),t("span",{class:"hljs-keyword"},"on"),e(` headings {
					id
					title
				}

				`),t("span",{class:"hljs-keyword"},"..."),e(),t("span",{class:"hljs-keyword"},"on"),e(` paragraphs {
					body
					background_color
				}
			}
		}
	}
}
`)])],-1),_t=t("div",{class:"tip hint"},[t("div",{class:"hint-title"},"Updating"),t("p",null,"Updating records in a many-to-any is identical to the other relationship types.")],-1),ft=t("h2",{id:"search-http-method"},[e("SEARCH HTTP Method "),t("a",{class:"header-anchor",href:"#search-http-method"},"#")],-1),mt=t("p",null,"When using the REST API to read multiple items by (very) advanced filters, you might run into the issue where the URL simply can\u2019t hold enough data to include the full query structure. In those cases, you can use the SEARCH http method as a drop-in replacement for GET, where you\u2019re allowed to put the query into the request body as follows:",-1),yt=t("p",null,[t("strong",null,"Before:")],-1),gt=t("pre",null,[t("code",null,`GET /items/articles?filter[title][_eq]=Hello World
`)],-1),bt=t("p",null,[t("strong",null,"After:")],-1),wt=t("pre",null,[t("code",{class:"language-json"},`SEARCH /items/articles

{
	"query": {
		"filter": {
			"title": {
				"_eq": "Hello World"
			}
		}
	}
}
`)],-1),Tt=t("p",null,[e("There\u2019s a lot of discussion around whether or not to put a body in a GET request, to use POSTs to create search queries, or to rely on a different method altogether. As of right now, we\u2019ve chosen "),t("a",{href:"https://datatracker.ietf.org/doc/draft-ietf-httpbis-safe-method-w-body/",target:"_blank",rel:"noopener noreferrer"},[e("to align with IETF\u2019s "),t("em",null,"HTTP SEARCH Method"),e(" specification")]),e(". While we recognize this is still a draft spec, the SEARCH method has been used extensively before in the WebDAV world ("),t("a",{href:"https://tools.ietf.org/html/rfc5323",target:"_blank",rel:"noopener noreferrer"},"spec"),e("), and compared to the other available options, it feels like the \u201Ccleanest\u201D and most correct to handle this moving forward. As with everything else, if you have any ideas, opinions, or concerns, "),t("a",{href:"https://github.com/directus/directus/discussions/new",target:"_blank",rel:"noopener noreferrer"},"we\u2019d love to hear your thoughts"),e(".")],-1),jt=t("p",null,"Useful reading:",-1),vt=t("ul",null,[t("li",null,[t("a",{href:"https://datatracker.ietf.org/doc/draft-ietf-httpbis-safe-method-w-body/",target:"_blank",rel:"noopener noreferrer"},[t("em",null,"HTTP SEARCH Method"),e(" (IETF, 2021)")])]),t("li",null,[t("a",{href:"https://httptoolkit.tech/blog/http-search-method/",target:"_blank",rel:"noopener noreferrer"},[t("em",null,"Defining a new HTTP method: HTTP SEARCH"),e(" (Tim Perry, 2021)")])]),t("li",null,[t("a",{href:"https://stackoverflow.com/questions/978061/http-get-with-request-body",target:"_blank",rel:"noopener noreferrer"},[t("em",null,"HTTP GET with request body"),e(" (StackOverflow, 2009 and ongoing)")])]),t("li",null,[t("a",{href:"https://www.elastic.co/guide/en/elasticsearch/guide/current/_empty_search.html",target:"_blank",rel:"noopener noreferrer"},[t("em",null,"Elastic Search GET body usage"),e(" (elastic, n.d.)")])]),t("li",null,[t("a",{href:"https://evertpot.com/dropbox-post-api/",target:"_blank",rel:"noopener noreferrer"},[t("em",null,"Dropbox starts using POST, and why this is poor API design."),e(" (Evert Pot, 2015)")])])],-1),Et=t("h2",{id:"system-data-in-graphql"},[e("System data in GraphQL "),t("a",{class:"header-anchor",href:"#system-data-in-graphql"},"#")],-1),At=t("p",null,[e("Due to restrictions in GraphQL itself, it\u2019s impossible to properly scope/namespace system functionality from regular data access. In order to prevent any naming conflicts between user-created and system data, we\u2019ve scoped the access to the two into two endpoints for user and system data respectively: "),t("code",null,"/graphql"),e(" and "),t("code",null,"/graphql/system"),e(". Both endpoints share the same underlying schema, so "),t("strong",null,"nested relations will work as expected"),e(" regardless if they \u201Ccross over\u201D between user and system data. The only difference in the two endpoints are the root query and mutation fields available.")],-1),kt=t("h2",{id:"error-codes"},[e("Error Codes "),t("a",{class:"header-anchor",href:"#error-codes"},"#")],-1),It=t("p",null,"Below are the global error codes used within Directus, and what they mean.",-1),Dt=t("table",null,[t("thead",null,[t("tr",null,[t("th",null,"Error Code"),t("th",null,"HTTP Status"),t("th",null,"Description")])]),t("tbody",null,[t("tr",null,[t("td",null,[t("code",null,"FAILED_VALIDATION")]),t("td",null,"400"),t("td",null,"Validation for this particular item failed")]),t("tr",null,[t("td",null,[t("code",null,"FORBIDDEN")]),t("td",null,"403"),t("td",null,"You are not allowed to do the current action")]),t("tr",null,[t("td",null,[t("code",null,"INVALID_CREDENTIALS")]),t("td",null,"401"),t("td",null,"Username / password or access token is wrong")]),t("tr",null,[t("td",null,[t("code",null,"INVALID_IP")]),t("td",null,"401"),t("td",null,"Your IP address isn\u2019t allow-listed to be used with this user")]),t("tr",null,[t("td",null,[t("code",null,"INVALID_OTP")]),t("td",null,"401"),t("td",null,"Wrong OTP was provided")]),t("tr",null,[t("td",null,[t("code",null,"INVALID_PAYLOAD")]),t("td",null,"400"),t("td",null,"Provided payload is invalid")]),t("tr",null,[t("td",null,[t("code",null,"INVALID_QUERY")]),t("td",null,"400"),t("td",null,"The requested query parameters can not be used")]),t("tr",null,[t("td",null,[t("code",null,"UNSUPPORTED_MEDIA_TYPE")]),t("td",null,"415"),t("td",null,[e("Provided payload format or "),t("code",null,"Content-Type"),e(" header is unsupported")])]),t("tr",null,[t("td",null,[t("code",null,"REQUESTS_EXCEEDED")]),t("td",null,"429"),t("td",null,"Hit the rate limit")]),t("tr",null,[t("td",null,[t("code",null,"ROUTE_NOT_FOUND")]),t("td",null,"404"),t("td",null,"Endpoint does not exist")]),t("tr",null,[t("td",null,[t("code",null,"SERVICE_UNAVAILABLE")]),t("td",null,"503"),t("td",null,"Could not use external service")]),t("tr",null,[t("td",null,[t("code",null,"UNPROCESSABLE_ENTITY")]),t("td",null,"422"),t("td",null,"You tried doing something illegal")])])],-1),Pt=t("div",{class:"warning hint"},[t("div",{class:"hint-title"},"Security"),t("p",null,[e("To prevent leaking which items exist, all actions for non-existing items will return a "),t("code",null,"FORBIDDEN"),e(" error.")])],-1),xt={setup(St,{expose:r}){const l={title:"API Reference",modularExtension:!1};return r({frontmatter:l}),(qt,Rt)=>{const s=o("router-link"),i=o("DocsWrapper");return d(),h(i,{frontmatter:l},{default:n(()=>[t("div",c,[u,p,_,t("p",null,[f,a(s,{to:"/docs/getting-started/introduction/#database-mirroring"},{default:n(()=>[m]),_:1}),y]),g,b,w,T,t("p",null,[j,a(s,{to:"/docs/reference/authentication/#access-tokens"},{default:n(()=>[v]),_:1}),E,a(s,{to:"/docs/getting-started/quickstart/#_6-set-role-public-permissions"},{default:n(()=>[A]),_:1}),k]),I,t("ul",null,[t("li",null,[a(s,{to:"/docs/reference/authentication/"},{default:n(()=>[D]),_:1})]),t("li",null,[a(s,{to:"/docs/reference/authentication/#login"},{default:n(()=>[P]),_:1})])]),S,t("p",null,[q,a(s,{to:"/docs/reference/query/#fields"},{default:n(()=>[R,L,x]),_:1}),C]),H,O,N,M,U,G,F,Q,V,B,Y,W,z,J,X,K,Z,$,tt,et,nt,st,at,lt,ot,rt,it,dt,ht,ct,ut,pt,_t,ft,mt,yt,gt,bt,wt,Tt,jt,vt,Et,At,kt,It,Dt,Pt])]),_:1})}}};export{xt as default};
