Start implementing the Northfork technology
Getting started
There are a few different options to implementing the Northfork Technology. For a quick-and-easy implementation, you can utilize our powerful Widgets, where you basically need to add a few lines of code to your site to leverage the full power of the Northfork Technology. If you’d rather create your own look and feel, you can instead utilize our JS SDK or API to build whatever interface you think is right for your users.
Regardless of the approach you use, the Northfork team will always be close by and ready to support you throughout the implementation phase.
-
Authentication
Northfork provides an API key to be used when communicating with our services. This is sent through the X-Custom-Auth header when making requests to our API.
curl -H 'X-Custom-Auth: {YOUR-API-KEY}'
API
This section shows a simple example of a user searching for recipes, resolving in-stock products for those recipes, and substituting a product for personal preference.
For more information, read our full API reference page here: https://developers.northfork.ai/reference
-
Discover
Get all recipes
Let’s start by fetching all recipes.
curl -X GET 'https://api.northfork.se/api/discovery/recipes' \ -H 'X-Custom-Auth: {YOUR-API-KEY}'
Find recipes that are “fully shopable”
For us to only get recipes that have all ingredients currently in stock, for a specified store, we can use the store_identifier filter.
curl -X GET 'https://api.northfork.se/api/discovery/recipes?store_identifier=100' \ -H 'X-Custom-Auth: {YOUR-API-KEY}'
Find vegetarian recipes
Now, let’s add a filter for only fetching vegetarian recipes.
curl -X GET 'https://api.northfork.se/api/discovery/recipes?store_identifier=100&diets=vegetarian' \ -H 'X-Custom-Auth: {YOUR-API-KEY}'
Find vegetarian recipes, with a maximum price per portion
And with a maximum portion price.
curl -X GET 'https://api.northfork.se/api/discovery/recipes?store_identifier=100&diets=vegetarian&max_portion_price=300' \ -H 'X-Custom-Auth: {YOUR-API-KEY}'
SDK – JS
Northfork provides a JS SDK to make the implementation as smooth as possible, but with the power of UI/UX in the partner’s hand. This SDK also handles cart states and store lookup, which makes utilising the full Northfork potential a breeze. Basically everything you need to build a full recipe shopping experience for your users.
-
Installation
Before We get into Northfork SDK you need to have NodeJS installed on your computer
this SDK supports both browsers as well as backend services that are running on NodeJSAfter you complete the installation of NodeJS you can start installing the NF SDK inside your project directory
https://www.npmjs.com/package/northfork-sdk – Link to the NPM registry -
Getting Started
Initialising the SDK
No need to save the response from the initialise , SKD will save and authorise user by itself after calling initialize.
import { initialize, getRecipe } from 'northfork-sdk' // Wait until initialisation will finish await initialize({ username: 'my-username', stage: 'development' })
Find Recipes
With the fetchRecipesFromDiscovery function you can fetch recipes using our Discovery API for Recipes, which allows you to use a whole set of different parameters to satisfy your needs.
You can read more about how this api works on our reference page here.
lets take a look at the example below
// Find recipes const response = await sdk.fetchRecipesFromDiscovery({ page, order_by, order_by_type, }); return response;
In the above example we are passing multiple parameters to the function.
For the parameters that can be passed to the function, keep in mind that, only the page number is required.
Default page/batch size of recipes fetched is 20 recipes.
Default order_by value is ‘id’ and order_by_type value ‘desc’
Discovery Recipe model
type discoveryRecipeRequest = { page: number filter?: string tags?: string order_by?: string order_by_type?: string diet?: string allergies?: string min_cooking_time?: string max_cooking_time?: string }
Get Recipe by Id
To get a recipe you will have to pass the recipeID to the getRecipe method(Code example below) it will return a Promise
note – this method excludes staples and shared ingredients
// Get recipe by id const recipe = await getRecipe('recipe-id') console.log('Got recipe', recipe.title)
Recipe model
type RecipeModel = { id: string; title: string; products: Product[]; portions: number; cookingTime: number; isBeingRemoved?: boolean; };
Create Cart
To Create a Cart you will need to pass the recipesIds to createCart method(Code example below) which will then return Promise
// Create Cart const cart = await createCart(recipesIds: string[]) console.log('Cart Created', cart)
Cart model
type RecipeModel = { id: string; title: string; products: Product[]; portions: number; cookingTime: number; isBeingRemoved?: boolean; };
-
Cart Actions
Now that we have an understanding of basic SDK functions lets take a deep look at advance cart actions.
Every CartAction returns a type CartActionResult which includes an object containing immediate cart state providing immediate update to a user and a promise with an eventual update from the API.type CartActionResult = { immeditateUpdate: CartModel; promise: Promise; };
Toggle Product
toggleProduct method can be used to include or exclude products from the cart, to use toggleProduct you will have to pass the cart object and the ID of the product that you want to include or exclude from the cart.
toggleProduct: (cart: CartModel, productId: number) => CartActionResult;
// Returns CartActionResult const updatedCart = toggleProduct(cart, productId); console.log('Updated Cart', updatedCart.immeditateUpdate) console.log('Updated Cart from the API', await updatedCart.promise)
Remove Recipe
NF SDK supports multiple recipes when creating a cart, to remove recipes from the cart you can use removeRecipe method ,which will accept the cart object and the ID of the recipe which you wish to remove.
removeRecipe: (cart: CartModel, recipeId: string) => CartActionResult;
// Returns CartActionResult const updatedCart = removeRecipe(cart, recipeId); console.log('Updated Cart', updatedCart.immeditateUpdate) console.log('Updated Cart from the API', await updatedCart.promise)
Set Recipe Portions
To change recipe portions you can use setPortions method, which accepts cart:CartModel , recipeId:string and the portions:number
setPortions: (cart: CartModel, recipeId: string, portions: number) => CartActionResult;
// Returns CartActionResult const updatedCart = setPortions(cart, recipeId, portions) console.log('Updated Cart', updatedCart.immeditateUpdate) console.log('Updated Cart from the API', await updatedCart.promise)
Increment Product
To increase quantity of a specific product incrementProduct method can be used.
incrementProduct: (cart: CartModel, productId: number) => CartActionResult | undefined;
// Returns CartActionResult const updatedCart = incrementProduct(cart, productId) console.log('Updated Cart', updatedCart.immeditateUpdate) console.log('Updated Cart from the API', await updatedCart.promise)
Decrement Product
To decrease quantity of a specific product decrementProduct method can be used.
decrementProduct: (cart: CartModel, productId: number) => CartActionResult | undefined;
// Returns CartActionResult const updatedCart = decrementProduct(cart, productId) console.log('Updated Cart', updatedCart.immeditateUpdate) console.log('Updated Cart from the API', await updatedCart.promise)
Get Substitutes
To get substitutes for a specific product you can use getSubstitutes method.
Keep in mind that getSubstitutes method does not return a CartActionResult instead it will return a Promise<Product[]>
getSubstitutes: (cart: CartModel, productId: number) => Promise<Product[]>;
const substitutes = await getSubstitutes(cart, productId) console.log('Substitute products',substitutes)
Swap Product
To swap a product with one of its substitutes swapProduct method can be used.
swapProduct: (cart: CartModel, currentProductId: number, newProduct: Product) => CartActionResult;
// Returns CartActionResult const updatedCart = swapProduct(cart, productId, newProductId) console.log('Updated Cart', updatedCart.immeditateUpdate) console.log('Updated Cart from the API', await updatedCart.promise)
-
Meal Planner
Meal planner is another functionality that we offer on our NF SDK, using meal planner using our SDK is very simple and straightforward.
Get Meal Planner
Lets start with getMealPlanner method, this method allows you to create a meal planner and retrieve recipe groups:
getMealPlanner: (mealPlannerRequest: MealPlannerRequest) => Promise<RecipeGroupResponse>;
There are two data types that might confuse you when you take a look at this meal planner functionality but the data types are explained below:
// Returns RecipeGroupResponse const recipeGroups = await getMealPlanner(mealPlannerRequest) console.log('recipeGroups',recipeGroups)
-
Nutrition
Using NF SDK you can get nutritional data to specific recipes of your choosing, to get nutritional data the existing getRecipe method can be used.
Get Recipe Nutrition
// Get recipe by id const recipe = await getRecipe('recipe-id') console.log('Got recipe', recipe.nutrition)
-
Price Calculation
With our built in getRecipePrices function you can calculate the total for a single recipe or multiple recipes.
getRecipePrices: (recipePriceRequest: RecipePriceRequest) => Promise<Number>;
Get Recipe Price
const recipePrice = await sdk.getRecipePrices({ storeId: storeId, recipes: [{ id: recipeId, portions: portions }], }); console.log('Recipe Total',recipePrice)
Note – the last two digits are cents ex:- 2356 => $23.56
SDK – Swift
Northfork Swift SDK
-
Getting Started
Initialising the SDK
NorthforkSDK.shared.initialize( config: InitializationConfig( username: "abc", stage: FlavorConfig.development) ) { isSucess in if (isSucess) { print("Success") } else { print("Failed") } }
Get Recipe
// Get recipe by id NorthforkSDK.shared.getRecipe(recipeId: "recipe-id") { it in if let _recipe = it.data { // Data } else { print(it.error?.errorMessage ?? "") } }
Create Cart
// Create Cart NorthforkSDK.shared.createCart(cartRequest: CartCreationRequest( zip : "12345", storeId : "100", recipes : [RecipeRequest(id : "recipe-id", portions : 4)] )) { it in // Update cart model cart = it.data if let _cart = cart { print("Create cart: \(_cart.id) Success") } else { print(it.error?.errorMessage ?? "") } }
-
Cart Actions
Remove Recipe
NorthforkSDK.shared.removeRecipe( cart: cart!, recipeId: "recipe-id" ) { newCart, response in isLoading = false // handle data guard response.error == nil else { resultText = response.error?.errorMessage ?? "" return } // Update new cart cart = newCart // Print data let data = response.data jsonEncoder.outputFormatting = .prettyPrinted let jsonData = try! jsonEncoder.encode(data) let json = String(data: jsonData, encoding: String.Encoding.utf8) resultText = json ?? "" }
Set Recipe Portions
NorthforkSDK.shared.setPortions( cart: cart!, recipeId: "recipe-id", portions: 4 ) { newCart, response in isLoading = false // handle data guard response.error == nil else { resultText = response.error?.errorMessage ?? "" return } // Update new cart cart = newCart // Print data let data = response.data jsonEncoder.outputFormatting = .prettyPrinted let jsonData = try! jsonEncoder.encode(data) let json = String(data: jsonData, encoding: String.Encoding.utf8) resultText = json ?? "" }
Increment Product
NorthforkSDK.shared.incrementProduct( cart: cart!, productId: 12345 ) { newCart, response in isLoading = false // handle data guard response.error == nil else { resultText = response.error?.errorMessage ?? "" return } // Update new cart cart = newCart let data = response.data jsonEncoder.outputFormatting = .prettyPrinted let jsonData = try! jsonEncoder.encode(data) let json = String(data: jsonData, encoding: String.Encoding.utf8) resultText = json ?? "" }
Decrement Product
NorthforkSDK.shared.decrementProduct( cart: cart!, productId: 12346 ) { newCart, response in isLoading = false // handle data guard response.error == nil else { resultText = response.error?.errorMessage ?? "" return } // Update new cart cart = newCart let data = response.data }
Get Substitutes
To get substitutes for a specific product you can use getSubstitutes method.
NorthforkSDK.shared.getSubstitutes( cart: cart!, productId: 12345 ) { it in isLoading = false guard it.error == nil else { resultText = it.error?.errorMessage ?? "" return } let products = it.data }
SDK – Kotlin
Northfork Kotlin SDK
-
Getting Started
Initialising the SDK
NorthforkSDK.initialize(this, InitializationConfig(username = "username", stage = FlavorConfig.DEVELOPMENT)) { if (it) { // initialize success } }
Get Recipe
// Get recipe by id NorthforkSDK.getRecipe(recipeId = "recipe1") { recipeResponse -> recipeResponse.error?.let { Toast.makeText(MainActivity@ this, it.errorMessage, Toast.LENGTH_SHORT).show() } recipeResponse.data?.let { } }
Create Cart
// Create Cart NorthforkSDK.createCart( CartCreationRequest(zip = "12345",storeId = "100", recipes = listOf(RecipeRequest(id = "recipe1", portions = 4))) ) { it.error?.let { } }
-
Cart Actions
Remove Recipe
NorthforkSDK.removeRecipe(cart = cart!!, recipeId = "recipe1") { pair -> pair.second.data?.let { // update new cart cart = pair.first // print val jsonString = gson.toJson(it) tvResult.text = jsonString } pair.second.error?.let { tvResult.text = it.errorMessage } }
Set Recipe Portions
NorthforkSDK.setPortions(cart = cart!!, recipeId = "recipe1", portions = 4) { pair -> pair.second.data?.let { // update new cart cart = pair.first // print result val jsonString = gson.toJson(it) tvResult.text = jsonString } pair.second.error?.let { tvResult.text = it.errorMessage } }
Increment Product
NorthforkSDK.incrementProduct(cart = cart!!, productId = 12345) { pair: Pair
> -> if (pair.second.data != null) { // update new cart cart = pair.first // print val jsonString = gson.toJson(pair.second.data) tvResult.text = jsonString } pair.second.error?.let { tvResult.text = it.errorMessage } } Decrement Product
NorthforkSDK.decrementProduct(cart = cart!!, productId = 12345) { pair: Pair
> -> if (pair.second.data != null) { // update new cart cart = pair.first // print val jsonString = gson.toJson(pair.second.data) tvResult.text = jsonString } pair.second.error?.let { tvResult.text = it.errorMessage } } Get Substitutes
To get substitutes for a specific product you can use getSubstitutes method.
NorthforkSDK.getSubstitutes(cart = cart!!, productId = 12345) { it.data?.let { val jsonString = gson.toJson(it) tvResult.text = jsonString } it.error?.let { tvResult.text = it.errorMessage } }
Web Widgets
Northfork widgets are a quick and easy way to start using the Northfork tools to enable recipe grocery shopping. The functionality is already built in with partners only required to add short javascript snippets to their web page.
Prerequisites
Markup for all widgets shall be placed where wanted rendered, with the exception being the Cart Widget.
Add Widgets library
<script id="nfw-v2" src=“https://widgets.northfork.se/dist/widgets.{user}.bundle.js”></script>
Add stylesheet
<link href=“http://widgets.northfork.se/dist/widgets.{user}.bundle.css” rel="stylesheet">
-
Cart Widget
Markup
<div id=“nfw-cart-container” [data-options]></div>
Support for multiple instantiations
No
Placement
Before </body>
Rendered
Predetermined
Data options
Options may support multiple values, separated by comma. The order is nonsignificant. See the “Multi” column for reference.
-
Meal Planner Widget
Markup
<div id=“nfw-menu-container” [data-options]></div>
Support for multiple instantiations
Yes
Placement
<body>
Rendered
Where placed
Data options
Options may support multiple values, separated by comma. The order is nonsignificant. See the “Multi” column for reference.
-
Nutrition Widget
Markup
<div class=“nfw-anchor” data-widget=“nutrition-info” [data-options]></div>
Support for multiple instantiations
No
Placement
<body>
Rendered
Where placed
Data options
Options may support multiple values, separated by comma. The order is nonsignificant. See the “Multi” column for reference.