{"id":138,"date":"2019-08-17T15:54:40","date_gmt":"2019-08-17T15:54:40","guid":{"rendered":"https:\/\/raminahmadi.co.uk\/?p=138"},"modified":"2025-07-11T16:25:03","modified_gmt":"2025-07-11T16:25:03","slug":"implementing-repository-pattern-with-sharepoint-component-library","status":"publish","type":"post","link":"https:\/\/codingwithramin.com\/?p=138","title":{"rendered":"Implementing Repository Pattern with SharePoint Framework Library Component"},"content":{"rendered":"\n<p>SharePoint framework 1.9.1 is out and Library Components are now generally available, Library component gives you ability to share code between your components.<\/p>\n\n\n\n<p>In this post I&#8217;m going to implement a basic version of repository pattern that allows you to do CRUD operations on SharePoint lists and libraries as well as tenant properties (global properties that can be shared between your components such as web parts, extensions and libraries).<\/p>\n\n\n\n<p>If you don&#8217;t want to read the post, the source code is available <a href=\"https:\/\/github.com\/AhmadiRamin\/ts-repository-library\">here<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Upgrade to SPFx 1.9.1 Version <\/h2>\n\n\n\n<p>Before we go further, make sure you upgrade your packages to SPFx 1.9.1, for more details please <a href=\"https:\/\/github.com\/SharePoint\/sp-dev-docs\/wiki\/SharePoint-Framework-v1.9.1-release-notes\">click here<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"> Create Library Component Type <\/h2>\n\n\n\n<p>Open PowerShell or command prompt and run Yeoman SharePoint Generator to create the solution.<\/p>\n\n\n\n<pre data-mode=\"powershell\" data-theme=\"twilight\" data-fontsize=\"14\" data-lines=\"Infinity\" class=\"wp-block-simple-code-block-ace\">yo @microsoft\/sharepoint<\/pre>\n\n\n\n<p><strong>Solution Name: <\/strong>ts-repository-libaray<\/p>\n\n\n\n<p><strong>Target for component:<\/strong>&nbsp;SharePoint\nOnline only (you can select other options based on your needs)<\/p>\n\n\n\n<p><strong>Place of files:<\/strong>&nbsp;We\nmay choose to use the same folder or create a subfolder for our solution.<\/p>\n\n\n\n<p><strong>Deployment option:<\/strong> Y<\/p>\n\n\n\n<p><strong>Permissions to access web APIs:<\/strong>&nbsp;N\n(as our solution doesn&#8217;t require permissions to access web APIs)<\/p>\n\n\n\n<p><strong>Type of client-side component to create:<\/strong>&nbsp;Library<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Creating Code Files <\/h2>\n\n\n\n<p>Open the solution with Visual Studio Code or any other editors, under repository folder create below structure:<\/p>\n\n\n\n<figure class=\"wp-block-image is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/raminahmadi.co.uk\/wp-content\/uploads\/2019\/08\/structure.png\" alt=\"files-structure\" class=\"wp-image-142\" width=\"430\" height=\"297\" srcset=\"https:\/\/codingwithramin.com\/wp-content\/uploads\/2019\/08\/structure.png 859w, https:\/\/codingwithramin.com\/wp-content\/uploads\/2019\/08\/structure-300x207.png 300w, https:\/\/codingwithramin.com\/wp-content\/uploads\/2019\/08\/structure-768x530.png 768w\" sizes=\"auto, (max-width: 430px) 100vw, 430px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Interfaces <\/h2>\n\n\n\n<p>We are going to create these interfaces under core folder:<\/p>\n\n\n\n<p><strong>IRead<\/strong>: defines the contacts for reading entities.<\/p>\n\n\n\n<pre data-mode=\"typescript\" data-theme=\"twilight\" data-fontsize=\"14\" data-lines=\"Infinity\" class=\"wp-block-simple-code-block-ace\">import IQueryOption from \".\/IQueryOption\";\n\nexport interface IRead&lt;T> {\n  getAll(): Promise&lt;T[]>;\n  getOne(id: number | string,options?:IQueryOption): Promise&lt;T>; \n}<\/pre>\n\n\n\n<p><strong>IWrite<\/strong>: contracts for add,update or delete entities.<\/p>\n\n\n\n<pre data-mode=\"typescript\" data-theme=\"twilight\" data-fontsize=\"14\" data-lines=\"Infinity\" class=\"wp-block-simple-code-block-ace\">export interface IWrite&lt;T> {\n    add(item: T): Promise&lt;any>;\n    update(item: T): Promise&lt;any>;\n    delete(id: number | string): Promise&lt;void>;\n}<\/pre>\n\n\n\n<p><strong>IQuery<\/strong>: queries we are going to implement to get data.<\/p>\n\n\n\n<pre data-mode=\"typescript\" data-theme=\"twilight\" data-fontsize=\"14\" data-lines=\"Infinity\" class=\"wp-block-simple-code-block-ace\">import { CamlQuery } from \"@pnp\/sp\/src\/types\";\nimport IQueryOption from \".\/IQueryOption\";\n\nexport default interface IQuery&lt;T>{\n    getItemsByCAMLQuery:(query: CamlQuery, ...expands: string[])=> Promise&lt;T[]>;\n    getItemsByQuery:(queryOptions: IQueryOption)=>Promise&lt;T[]>;\n}<\/pre>\n\n\n\n<p><strong>IQueryOption<\/strong>: provides options to filter, expand, select, number of items to return etc.<\/p>\n\n\n\n<pre data-mode=\"typescript\" data-theme=\"twilight\" data-fontsize=\"14\" data-lines=\"Infinity\" class=\"wp-block-simple-code-block-ace\">export default interface IQueryOption{\n    select?: string[];\n    filter?:string;\n    expand?:string[];\n    top?:number;\n    skip?:number;\n}<\/pre>\n\n\n\n<p><strong>IListItem<\/strong>: describe the basic properties of SharePoint list items.<\/p>\n\n\n\n<pre data-mode=\"typescript\" data-theme=\"twilight\" data-fontsize=\"14\" data-lines=\"Infinity\" class=\"wp-block-simple-code-block-ace\">export default interface IListItem{\n    Id:number;\n}<\/pre>\n\n\n\n<p><strong>ITenantProperty<\/strong>: tenant property keys.<\/p>\n\n\n\n<pre data-mode=\"typescript\" data-theme=\"twilight\" data-fontsize=\"14\" data-lines=\"Infinity\" class=\"wp-block-simple-code-block-ace\">export interface ITenantProperty {\n  key: string;\n  Comment?: string;\n  Description?: string;\n  Value: string;\n}<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">SharePoint repository <\/h2>\n\n\n\n<p>Under SharePoint folder we have two files:<\/p>\n\n\n\n<p><strong>ISharePointBaseRepository<\/strong>: this interface implements IRead, IWrite and IQuery, it&#8217;s always recommended to seperate the responsibilities, if you are not familiar with SOLID principles I suggest you <a href=\"https:\/\/itnext.io\/solid-principles-explanation-and-examples-715b975dcad4\">this post<\/a>.<\/p>\n\n\n\n<pre data-mode=\"typescript\" data-theme=\"twilight\" data-fontsize=\"14\" data-lines=\"Infinity\" class=\"wp-block-simple-code-block-ace\">\/\/ import all interfaces\nimport { IWrite } from '..\/core\/IWrite';\nimport { IRead } from '..\/core\/IRead';\nimport IListItem from '..\/core\/IListItem';\nimport IQuery from '..\/core\/IQuery';\n\/\/ that class only can be extended\nexport interface ISharePointBaseRepository&lt;T extends IListItem> extends IWrite&lt;T>, IRead&lt;T>,IQuery&lt;T> {\n\n}<\/pre>\n\n\n\n<p><strong>SharePointBaseRepository<\/strong>: concrete class that implements the ISharePointBaseRepository interface.<\/p>\n\n\n\n<pre data-mode=\"typescript\" data-theme=\"twilight\" data-fontsize=\"14\" data-lines=\"Infinity\" class=\"wp-block-simple-code-block-ace\">import { sp, ItemAddResult, ItemUpdateResult, List, SPRest, Web } from \"@pnp\/sp\";\nimport { ISharePointBaseRepository } from \".\/ISharePointBaseRepository\";\nimport IListItem from \"..\/core\/IListItem\";\nimport IQueryOption from \"..\/core\/IQueryOption\";\n\nexport default class SharePointRepository&lt;T extends IListItem> implements ISharePointBaseRepository&lt;T>{\n    protected _list: List;\n    protected _web: Web;\n    protected _sp: SPRest;\n\n    constructor(listId: string, webUrl?: string) {\n        this._web = webUrl ? new Web(webUrl) : this._web = sp.web;\n        this._list = this._web.lists.getById(listId);\n        this._sp = sp;\n    }\n\n    \/\/ Add new entity to collection\n    public async add(item: Omit&lt;T, \"Id\">): Promise&lt;ItemAddResult> {\n        return this._list.items.add(item);\n    }\n\n    \/\/ Update an existing entity\n    public async update(item: T): Promise&lt;ItemUpdateResult> {\n        const updatingItem: Omit&lt;T, \"Id\"> = item;\n        return this._list.items.getById(item.Id).update(updatingItem);\n    }\n\n    \/\/ Remove an entity\n    public async delete(id: number): Promise&lt;void> {\n        return this._list.items.getById(id).delete();\n    }\n\n    \/\/ Get all items\n    public async getAll(): Promise&lt;T[]> {\n        try {\n            const items = await this._list.items.getAll();\n            return items;\n        }\n        catch (error) {\n            return Promise.reject(error.message);\n        }\n    }\n\n    \/\/ Get one by Id, optional query options\n    public async getOne(id: number, queryOptions?: Omit&lt;IQueryOption, \"top\" | \"filter\">): Promise&lt;T> {\n        let result = this._list.items.getById(id);\n        if (queryOptions) {\n            if (queryOptions.expand)\n                result = result.expand(...queryOptions.expand);\n            if (queryOptions.select)\n                result = result.select(...queryOptions.select);\n        }\n        try {\n            const item = await result.get();\n            return item;\n        }\n        catch (error) {\n            return Promise.reject(error.message);\n        }\n    }\n\n    \/\/ Get items using CAML query\n    public async getItemsByCAMLQuery(query: import(\"@pnp\/sp\").CamlQuery, ...expands: string[]): Promise&lt;T[]> {\n        return this._list.getItemsByCAMLQuery(query, ...expands);\n    }\n\n    \/\/ Get items using query options\n    public async getItemsByQuery(queryOptions: IQueryOption): Promise&lt;T[]> {\n        const { filter, select, expand, top, skip } = queryOptions;\n        let result = this._list.items;\n        if (filter) result = result.filter(filter);\n        if (select) result = result.select(...select);\n        if (expand) result = result.expand(...expand);\n        if (top) result = result.top(top);\n        if (skip) result = result.skip(skip);\n        return result.get();\n    }\n}<\/pre>\n\n\n\n<ul class=\"wp-block-list\"><li>Omit helper type added to TypeScript 3.5, <a href=\"https:\/\/devblogs.microsoft.com\/typescript\/announcing-typescript-3-5-rc\/#the-omit-helper-type\">click here<\/a> for more information.<\/li><li>There are 3 protected members in this class that help you extend it without changing the class! (Open\/closed principle)<\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Tenant repository <\/h2>\n\n\n\n<p>Like SharePoint repository, we have two files unders tenant folder.<\/p>\n\n\n\n<p><strong>ITenantBaseRepository<\/strong>: contracts for reading and writing of tenant properties.<\/p>\n\n\n\n<pre data-mode=\"typescript\" data-theme=\"twilight\" data-fontsize=\"14\" data-lines=\"Infinity\" class=\"wp-block-simple-code-block-ace\">\/\/ import all interfaces\nimport { IWrite } from '..\/core\/IWrite';\nimport { IRead } from '..\/core\/IRead';\n\/\/ that class only can be extended\nexport interface ITenantBaseRepository&lt;T> extends IWrite&lt;T>, IRead&lt;T> {}<\/pre>\n\n\n\n<p>TenantRepository: concerete class implements ITenantBaseRepository.<\/p>\n\n\n\n<pre data-mode=\"typescript\" data-theme=\"twilight\" data-fontsize=\"14\" data-lines=\"Infinity\" class=\"wp-block-simple-code-block-ace\">import { sp, SPRest, Web, StorageEntity } from \"@pnp\/sp\";\nimport { ITenantBaseRepository } from \".\/ITenantBaseRepository\";\nimport { WebPartContext } from \"@microsoft\/sp-webpart-base\";\nimport { ExtensionContext } from '@microsoft\/sp-extension-base';\nimport { ITenantProperty } from \"..\/core\/ITenantProperty\";\nimport { SPHttpClientResponse, SPHttpClient } from \"@microsoft\/sp-http\";\n\nexport default class TenantBaseRepository&lt;T extends StorageEntity &amp; ITenantProperty> implements ITenantBaseRepository&lt;T>{\n    protected _sp: SPRest;\n    protected _context: WebPartContext | ExtensionContext;\n    private appCatalogUrl:string;\n    \/\/ Constructor\n    constructor(context: WebPartContext | ExtensionContext) {\n        \/\/ Setuo Context to PnPjs\n        sp.setup({\n            spfxContext: context\n        });\n        this._sp = sp;\n        this._context = context;\n    }\n    \/\/ Add tenant property\n    public async add(property: ITenantProperty): Promise&lt;void> {\n        const appCatalogWeb: Web= await this.getAppCatalogWeb();\n        return appCatalogWeb.setStorageEntity(property.key, property.Value, property.Description, property.Comment);\n    }\n\n    \/\/ Update tenant property\n    public async update(newProperty: ITenantProperty): Promise&lt;void>{\n        return this.add(newProperty);\n    }\n\n    \/\/ Remove tenant property\n    public async delete(key:string): Promise&lt;void> {\n        const appCatalogWeb: Web= await this.getAppCatalogWeb();\n        return appCatalogWeb.removeStorageEntity(key);\n    }\n\n    \/\/ Get all properties\n    public async getAll(): Promise&lt;T[]> {\n        try {\n            const catalogUrl =await this.getAppCatalogUrl();\n            const apiUrl = `${catalogUrl}\/_api\/web\/AllProperties?$select=storageentitiesindex`;\n            const data: SPHttpClientResponse = await this._context.spHttpClient.get(apiUrl, SPHttpClient.configurations.v1);\n            if (data.ok) {                \n                const results = await data.json();\n                \n                if (results &amp;&amp; results.storageentitiesindex) {                    \n                    const parsedData:{ [key: string]: ITenantProperty } = JSON.parse(results.storageentitiesindex);\n                    \n                    const keys: string[] = Object.keys(parsedData);\n                    let properties : ITenantProperty[] = [];\n                    keys.map((key: string): any => {\n                        const property: ITenantProperty = parsedData[key];\n                        properties.push(\n                          {\n                            key,\n                            Value: property.Value,\n                            Description: property.Description,\n                            Comment: property.Comment\n                          }\n                        );\n                      });\n                    return properties as T[];\n                }\n            }\n            return null;\n        } catch (error) {\n            return Promise.reject(error.message);\n        }\n    }\n\n    \/\/ Get tenant property\n    public async getOne(key:string): Promise&lt;T> {\n        const appCatalogWeb: Web= await this.getAppCatalogWeb();\n        try{\n            const property = await appCatalogWeb.getStorageEntity(key);\n            return {...property,key} as T;\n        }\n        catch(error){\n            return Promise.reject(error.message);\n        }\n         \n    }\n\n    \/\/ Get App Catalog\n    private async getAppCatalogUrl() {\n        if(this.appCatalogUrl)\n            return this.appCatalogUrl;\n        try {\n            const appCatalog: Web = await this._sp.getTenantAppCatalogWeb();\n            const appCatalogWeb = await appCatalog.get();\n            this.appCatalogUrl=appCatalogWeb.Url;\n            return appCatalogWeb.Url;\n        } catch (error) {\n            console.dir(error);\n            return Promise.reject(error.message);\n        }\n    }\n    \/\/ Get App Catalog web\n    private async getAppCatalogWeb():Promise&lt;Web>{\n        this.appCatalogUrl = await this.getAppCatalogUrl();\n        return new Web(this.appCatalogUrl);\n    }\n\n}<\/pre>\n\n\n\n<p>Now we need to add above concerete classes to the index file so they can be accessible from our components. Open index.ts file and update it to:<\/p>\n\n\n\n<pre data-mode=\"typescript\" data-theme=\"twilight\" data-fontsize=\"14\" data-lines=\"Infinity\" class=\"wp-block-simple-code-block-ace\">export { RepositoryLibrary } from '.\/libraries\/repository\/RepositoryLibrary';\nexport {default as TenantRepository} from \".\/libraries\/repository\/repositories\/tenant\/TenantRepository\";\nexport {default as SharePointRepository} from \".\/libraries\/repository\/repositories\/sharepoint\/SharePointRepository\";<\/pre>\n\n\n\n<p>Run <mark><span style=\"background-color:#eeeeee\" class=\"tadv-background-color\">gulp build<\/span><\/mark> to make sure you don&#8217;t have any errors, then run<mark><span style=\"color:#313131\" class=\"tadv-color\"><span style=\"background-color:#eeeeee\" class=\"tadv-background-color\"> npm link<\/span><\/span><\/mark> to  create a local npm link to the library with the name which is provided in the&nbsp;<code>package.json<\/code>. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Consume the library for local testing <\/h2>\n\n\n\n<ul class=\"wp-block-list\"><li> Create a web part or an extension solution in a separate folder. <\/li><li> &nbsp;Run the command&nbsp;<code>npm link ts-repository-library<\/code>.<\/li><li> Add an import to refer to the library: <\/li><\/ul>\n\n\n\n<pre data-mode=\"typescript\" data-theme=\"twilight\" data-fontsize=\"14\" data-lines=\"Infinity\" class=\"wp-block-simple-code-block-ace\">import * as Repository from \"ts-repository-library\";<\/pre>\n\n\n\n<p>Now the repositories available to be consumed:<\/p>\n\n\n\n<pre data-mode=\"typescript\" data-theme=\"twilight\" data-fontsize=\"14\" data-lines=\"Infinity\" class=\"wp-block-simple-code-block-ace\">interface ISampleItem {\n  Id: number;\n  Title: string;\n}\n\nconst sampleListId = this.props.listId;\nconst sampleList = new Repository.SharePointRepository&lt;ISampleItem>(sampleListId);\nconst tenantRepository = new Repository.TenantRepository(this.props.spfxContext);\n\n<\/pre>\n\n\n\n<p>Try some of methods by calling them:<\/p>\n\n\n\n<pre data-mode=\"typescript\" data-theme=\"twilight\" data-fontsize=\"14\" data-lines=\"Infinity\" class=\"wp-block-simple-code-block-ace\">\/\/ get all items in the list\nconst items = await this.sampleList.getAll();\n\n\/\/ get item by id\nconst item = await this.sampleList.getOne(3);\n\n\/\/ add new item\nawait this.sampleList.add({\n      Title: \"New Item\"\n    });\n\n\/\/ update an Item\nawait this.sampleList.update({\n      Id: 3,\n      Title: \"Updated value\"\n    });\n    \n\/\/ remove an item\nawait this.sampleList.delete(3);\n\n\/\/ get all tenant properties\nconst properties = await this.tenantRepository.getAll();\n\n\/\/ get Property by key\nconst property = await this.tenantRepository.getOne(\"New Tenant Property\");\n\n\/\/ add new Property\nawait this.tenantRepository.add({\n      key:\"New Tenant Property\",\n      Value:\"New Tenant Value\"\n    });\n\n\/\/ update a Property\nawait this.tenantRepository.update({\n      key:\"New Tenant Property\",\n      Value:\"Value has been updated\"\n    });\n    \n\/\/ remove a Property\nawait this.tenantRepository.delete(\"New Tenant Property\");<\/pre>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"758\" height=\"394\" src=\"https:\/\/raminahmadi.co.uk\/wp-content\/uploads\/2019\/08\/screenshot.gif\" alt=\"\" class=\"wp-image-155\"\/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Deployment <\/h2>\n\n\n\n<p>To deploy the library to app catalog run the following commands:<\/p>\n\n\n\n<pre data-mode=\"powershell\" data-theme=\"twilight\" data-fontsize=\"14\" data-lines=\"Infinity\" class=\"wp-block-simple-code-block-ace\">gulp bundle --ship\ngulp package-solution --ship<\/pre>\n\n\n\n<p>Then deploy the spkg file to your tenant app catalog and make it available to all site collection.<\/p>\n\n\n\n<p>To deploy the web part or extension that is consuming the library, you need to update the package.json and add your library component as a dependency:<\/p>\n\n\n\n<pre data-mode=\"json\" data-theme=\"twilight\" data-fontsize=\"14\" data-lines=\"Infinity\" class=\"wp-block-simple-code-block-ace\">\"dependencies\": {\n    \"ts-repository-library\": \"0.0.1\", \/\/ here we added the reference to the library\n    \"@microsoft\/sp-core-library\": \"1.9.1\",\n    ...\n},<\/pre>\n\n\n\n<p>Now you can bundle and package the solution and deploy it to your tenant app catalog.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion <\/h2>\n\n\n\n<p>Library Component is the new way of resuing and sharing code across our components and with the power of TypeScript you can have clean, high quality and strongly typed code helps you implementing SOLID design patterns into a language that doesn\u2019t really support it!<\/p>\n\n\n\n<p>Next step to make this library even better is to add more resuable methods or a new repository to handle Graph API requests.<\/p>\n\n\n\n<p>Thank you for reading this post and hope you enjoyed!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>SharePoint framework 1.9.1 is out and Library Components are now generally available, Library component gives you ability to share code between your components. In this post I&#8217;m going to implement a basic version of repository pattern that allows you to do CRUD operations on SharePoint lists and libraries as well as tenant properties (global properties that can be shared between your components such as web parts, extensions and libraries). If you don&#8217;t want to read&hellip;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2,4,3],"tags":[12,13,15,7,11,14],"class_list":["post-138","post","type-post","status-publish","format-standard","hentry","category-office-365","category-sharepoint-framework","category-sharepoint-online","tag-librarycomponent","tag-office365","tag-repository-pattern","tag-sharepoint","tag-spfx","tag-typescript"],"_links":{"self":[{"href":"https:\/\/codingwithramin.com\/index.php?rest_route=\/wp\/v2\/posts\/138","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/codingwithramin.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/codingwithramin.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/codingwithramin.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/codingwithramin.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=138"}],"version-history":[{"count":26,"href":"https:\/\/codingwithramin.com\/index.php?rest_route=\/wp\/v2\/posts\/138\/revisions"}],"predecessor-version":[{"id":170,"href":"https:\/\/codingwithramin.com\/index.php?rest_route=\/wp\/v2\/posts\/138\/revisions\/170"}],"wp:attachment":[{"href":"https:\/\/codingwithramin.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=138"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/codingwithramin.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=138"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/codingwithramin.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=138"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}