Slug Is Not Working While Importing External Data (MongoDB Database)

6 replies
Last updated: Feb 15, 2021
Hello, Slug is not working while importing external data (MongoDB database). I am following the guide “Importing data from external data into Sanity”. Everyting works fine, exept for generating slug. This slug exists in my schema, but does not exist in the external data.{
name: "slug",
title: "Slug",
type: "slug",
options: {
source: "title",
},
}
Now I am trying to generate the slug from the "title" of the external data, while executing a slugify function on that "title". Obviously this leads to using the wrong _type: "string", instead of _type: "slug". What can I do to solve this?
AI Update

The issue you're experiencing is because the slug field in Sanity has a specific data structure. When you're importing data and trying to set a slug field, you can't just set it as a string value - you need to provide it in the correct format.

A slug field in Sanity is actually an object with a _type of "slug" and a current property that contains the actual slug string. So when importing your MongoDB data, you need to structure it like this:

{
  title: "Your Title from MongoDB",
  slug: {
    _type: "slug",
    current: slugify(yourMongoDBTitle) // your slugified string goes here
  }
}

Here's a practical example of how to handle this in your import script:

import slugify from 'slugify' // or use any slugification library

// When mapping your MongoDB data to Sanity documents
const sanityDocument = {
  _type: 'yourDocumentType',
  title: mongoDocument.title,
  slug: {
    _type: 'slug',
    current: slugify(mongoDocument.title, {
      lower: true,
      strict: true
    })
  },
  // ... other fields
}

The key points are:

  1. The slug field must be an object, not a string
  2. It must have _type: "slug"
  3. The actual slug string goes in the current property

As mentioned in the Sanity documentation on slug generation via API, the automatic slug generation feature only works within Sanity Studio. When importing data programmatically, you need to generate the slugs yourself. Sanity internally uses the speakingurl library, but you can use any slugification library like slugify or write your own function.

If you want to use the same slugification logic that Sanity uses, you could install speakingurl:

npm install speakingurl

Then use it in your import:

import getSlug from 'speakingurl'

slug: {
  _type: 'slug',
  current: getSlug(mongoDocument.title)
}

This should resolve your _type mismatch and properly create slug fields during your import process from MongoDB.

Hi Pieter, sounds like you're making good progress on the migration from MongoDB. For the slug, is it helpful to know that the generation function only works inside the studio? It gives you a 'Generate' button that you can use to generate a slug from the source field,
title
in this case.
To generate slugs during import, you will need to replicate the functionality of the slug field. Our own slug function uses
speakingurl
under the hood: https://github.com/pid/speakingurl . Here's the function itself as an example: https://github.com/sanity-io/sanity/blob/ebd72b17236d29f6dad636c17e3ddd1b301e0853/[…]ackages/%40sanity/form-builder/src/inputs/Slug/utils/slugify.ts
Thnx for your reply!I think apart from the slugify function there is also the wrong type involved.
You think, my own simple slugify function would not work?
Here it is:
(to transform the data I am using node-json-transform)
var baseMap = {
item: {
_id: "_id",
title: "title",
slug: "title",
},
operate: [
{
run: function slugify(text) {
return text
.toString()
.toLowerCase()
.replace(/\s+/g, "-")
.replace(/\.+/g, "")
.replace(/’+/g, "-")
.replace(/â+/g, "a")
.replace(/'+/g, "-");
},
on: "slug",
},
],
};
Transaction:
fetch(apiURL)
.then((res) => res.json())
.then((res) => transform(res, baseMap))
.then ... transaction.createOrReplace(document)
This results in "content has invalid type"
Have you tried setting
_type: "slug"
(mind the underscore
_
in front of
type
) on the item?
Brilliant, it works!I have been playing with that option.
But forgot to set the "current" property (instead of "name").
slug: {
current: "title",
},
I also struggled a lot with nested data.
That is where node-json-transform comes in handy.
You should mention this of some sort in the docs about importing external data, I think.
Besides I like Sanity a lot!
Cheers.
Awesome! And thanks - that's great feedback! Admittedly, there's not that much material yet on how to import external data, and I think you're one of the first people I've seen that undertake this from MongoDB to Sanity. We can definitely extend the documentation there.
Happy you're able to move on
🙂
Welcome.You are problably going to need some method to add unique id's too.
Here is how I did the job, totally:
const sanityClient = require("@sanity/client");
const fetch = require("node-fetch");
const { transform } = require("node-json-transform");
const crypto = require("crypto");
function unique() {
const uniqueid = crypto.randomBytes(16).toString("hex");
return uniqueid;
}
const client = sanityClient({
projectId: <projectId>,
dataset: "production",
token: <token>,
useCdn: false,
});
const apiURL = <apiURL>;
var baseMap = {
item: {
_id: "_id",
title: "title",
slug: {
current: "title",
},
dish: {
_ref: "dish._id",
},

tag: "tags",
freshItems: "fresh",
stockItems: "stock",
directions: "directions",
relatedRecipes: "related",
cookbook: {
_ref: "book._id",
},
info: "info",
},
each: function (item) {
item._type = "recipe";
item.slug._type = "slug";
item.dish._type = "reference";
item.book._type = "reference";
return item;
},
operate: [
{
run: function (ary) {
return transform(ary, nestedTag);
},
on: "tag",
},
{
run: function (ary) {
return transform(ary, nestedFresh);
},
on: "fresh",
},
{
run: function (ary) {
return transform(ary, nestedStock);
},
on: "stock",
},
{
run: function (ary) {
return transform(ary, nestedDirections);
},
on: "directions",
},
{
run: function (ary) {
return transform(ary, nestedRelated);
},
on: "related",
},
{
run: function slugify(text) {
return text
.toString()
.toLowerCase()
.replace(/\s+/g, "-")
.replace(/\.+/g, "")
.replace(/’+/g, "-")
.replace(/â+/g, "a")
.replace(/'+/g, "-");
},
on: "slug.current",
},
],
};
var nestedTag = {
item: {
_ref: "_id",
},
each: function (item) {
item._type = "reference";
item._key = unique();
return item;
},
};
var nestedFresh = {
item: {
_id: "_id",
name: "name",
quantity: "quantity",
unit: "unit",
ingredient: "ingredient",
},
each: function (item) {
item._type = "ingredient";
item._key = unique();
return item;
},
};
var nestedStock = {
item: {
_id: "_id",
name: "name",
quantity: "quantity",
unit: "unit",
ingredient: "ingredient",
},
each: function (item) {
item._type = "ingredient";
item._key = unique();
return item;
},
};
var nestedDirections = {
item: {
_id: "_id",
name: "name",
},
each: function (item) {
item._type = "list";
item._key = unique();
return item;
},
};
var nestedRelated = {
item: {
_ref: "_id",
},
each: function (item) {
item._type = "reference";
item._key = unique();
return item;
},
};
fetch(apiURL)
.then((res) => res.json())
.then((res) => transform(res, baseMap))
.then((documents) => {
let transaction = client.transaction();
documents.forEach((document) => {
transaction.createOrReplace(document);
});
return transaction.commit();
});

Sanity – Build the way you think, not the way your CMS thinks

Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.

Was this answer helpful?