4.6.x node.module node_save($node)
4.7.x node.module node_save(&$node)
5.x node.module node_save(&$node)
6.x node.module node_save(&$node)
7.x node.module node_save($node)

Save a node object into the database.

9 calls to node_save()
blogapi_blogger_edit_post in modules/blogapi/blogapi.module
Blogging API callback. Modifies the specified blog node.
blogapi_blogger_new_post in modules/blogapi/blogapi.module
Blogging API callback. Inserts a new blog post as a node.
blogapi_mt_publish_post in modules/blogapi/blogapi.module
Blogging API callback. Publishes the given node
blogapi_mt_set_post_categories in modules/blogapi/blogapi.module
Blogging API callback. Assigns taxonomy terms to a particular node.
book_admin_edit_submit in modules/book/book.admin.inc
Handle submission of the book administrative page form.

... See full list


modules/node/node.module, line 875
The core that allows content to be submitted to the site. Modules and scripts may programmatically submit nodes using the usual form API pattern.


function node_save(&$node) {

  // Let modules modify the node before it is saved to the database.
  node_invoke_nodeapi($node, 'presave');
  global $user;

  // Insert a new node.
  $node->is_new = empty($node->nid);
  if ($node->is_new || !empty($node->revision)) {

    // When inserting a node, $node->log must be set because
    // {node_revisions}.log does not (and cannot) have a default
    // value.  If the user does not have permission to create
    // revisions, however, the form will not contain an element for
    // log so $node->log will be unset at this point.
    if (!isset($node->log)) {
      $node->log = '';
  elseif (empty($node->log)) {

    // When updating a node, however, avoid clobbering an existing
    // log entry with an empty one.

  // For the same reasons, make sure we have $node->teaser and
  // $node->body set.
  if (!isset($node->teaser)) {
    $node->teaser = '';
  if (!isset($node->body)) {
    $node->body = '';

  // Save the old revision if needed.
  if (!$node->is_new && !empty($node->revision) && $node->vid) {
    $node->old_vid = $node->vid;
  $time = time();
  if (empty($node->created)) {
    $node->created = $time;

  // The changed timestamp is always updated for bookkeeping purposes (revisions, searching, ...)
  $node->changed = $time;
  $node->timestamp = $time;
  $node->format = isset($node->format) ? $node->format : FILTER_FORMAT_DEFAULT;

  // Generate the node table query and the node_revisions table query.
  if ($node->is_new) {
    _node_save_revision($node, $user->uid);
    drupal_write_record('node', $node);
    db_query('UPDATE {node_revisions} SET nid = %d WHERE vid = %d', $node->nid, $node->vid);
    $op = 'insert';
  else {
    drupal_write_record('node', $node, 'nid');
    if (!empty($node->revision)) {
      _node_save_revision($node, $user->uid);
      db_query('UPDATE {node} SET vid = %d WHERE nid = %d', $node->vid, $node->nid);
    else {
      _node_save_revision($node, $user->uid, 'vid');
    $op = 'update';

  // Call the node specific callback (if any).
  node_invoke($node, $op);
  node_invoke_nodeapi($node, $op);

  // Update the node access table for this node.

  // Clear the page and block caches.


monaw’s picture

node_save() will modify the argument so you can do a var_dump of the argument and see the entire data structure that was just saved.

Also, node_save() will update the node given if it exists already.

steve.m’s picture

Note that you need to provide $node->vid to update an existing node with node_save()

mattcasey’s picture

I was also able to check 'make new revision' under Workflow defaults for my content type instead of setting the vid.

apotek’s picture

I see this function returns no TRUE/FALSE, which means it's impossible for the caller to determine if all the operations involved in saving the node were successful or not.

This strikes me as a big omission.

Does anyone have a good workaround for this? Is there a global variable, or even one in the node that can be checked for a return value?

bacon’s picture

the node_save function will modify the node that you pass into it. So if you check for $node->nid, that field should be set after the node_save function is called.

I think that should do it for you.

tmsimont’s picture

that would only work if you are creating a new node... if you've first used node_load, and then use node_save to update an exisiting node, the $node->nid will be set even if node_save fails...

is $node->vid reliable?

or perhaps in this scenario you could do a check on $node->changed before save and after save.

however, as apotek mentioned, it is kinda lame this doesn't just return it's success...

jefkin’s picture

Not ideal, of course, but if you use node_load, as you say, then node_save, use a copy of your original node_load, and compare the $node->changed:

$orig = node_load(); // load original
$node = $orig;       // copy for mods

$node->teaser = 'nah, nah, nah, nah, boo, boo'; // Do some changes

node_save($node); // save your changes

if ($node->changed != $orig->changed)
  drupal_set_message('Unable to update.');

If your node type is saving revisions, you could technically use $node->vid instead of $node->changed in the same manner, however if your node type ISN'T saving revisions, you need to use $node->changed.

OliverColeman’s picture

Comparing the 'changed' member of the node object before and after a call to node_save doesn't tell if you the DB was actually updated as this member is updated before any DB queries are executed. Since node_save doesn't check if the queries were executed properly (as someone else noted quite an omission!)

A slight modification of this approach that will work (famous last words) is to compare the original value of 'changed' before the call to the 'changed' value directly queried from the DB (or by calling node_load($node->nid, NULL, TRUE) to reload the entire node direct from the DB, but just for checking to see if the node was saved this could be relatively expensive if there are other modules loading additional data into the node object). eg:

function my_save_node(&$node) {
  $changed_orig = isset($node->changed) ? $node->changed : NULL;
  // If the node is new but didn't save.
  if (!$node->nid) {
    return FALSE;
  $changed_new = db_result(db_query("SELECT changed FROM {node} WHERE nid=%d", $nid));
  // If the node wasn't new and didn't save.
  if ($changed_orig == $changed_new) {
    return FALSE;
  return TRUE;
Fabianx’s picture

Drupal by default does not update the node_load cache.

Assuming a node has the title of "original" do:

   $node = node_load($nid);
   $node->title = "changed";

   $node2 = node_load($nid);
   var_dump ($node2->title == $node->title);

and this gives "FALSE";

So make sure that if you have a sequence of: node_load, node_save, node_load that you either load the node by

  $node = node_load(array("nid" => $nid));

to fetch it from DB or clear the node_load cache afterwards with:

  node_load(FALSE, NULL, TRUE);

More information on this core limitation can be found here:


This is fixed in D7.

doubledutch’s picture

I decided some time ago that CCK has so many advantages that it is the preferred way to store data even when it involves high transactional load.

But many node_save's will lead to many cache_clear_all so it would appear logical to me to extend the constructor of this function to allow the cache to remain intact if the developer knows it's OK to do so:

function node_save(&$node,$clear_cache=true)

would be my suggestion so I can happily stick to what I'm doing and avoid a dramatic reduction in performance

Has this ever been considered?

penyaskito’s picture

If you want this to be considered, you should post it as an issue in the Core Issue Queue (http://drupal.org/node/add/project-issue/drupal)

John Carbone’s picture

I never realized this before now, but if you need the node id of a new node you just created and saved, since the node is passed by reference to node_save, your node will have the new nid in $node->nid once you call node_save.